Я пытаюсь написать декоратор заморозки для Python.
Идея заключается в следующем:
(в ответ на два комментария)
Я могу ошибаться, но я думаю, что есть два основных варианта использования
контрольный пример.
Одним из них является разработка, основанная на тестировании:
В идеале разработчики пишут кейс перед написанием кода.
Обычно это помогает определить архитектуру, потому что эта дисциплина
заставляет определять реальные интерфейсы до разработки.
Можно даже считать, что в некоторых случаях человек, который
отправляет задание между dev пишет тестовый пример и
используйте его, чтобы эффективно проиллюстрировать спецификацию, которую он имеет в виду.
У меня нет опыта использования такого теста.
Вторым является идея, что все проекты с приличным
Размер и несколько программистов страдает от неработающего кода.
Что-то, что используется для работы, может сломаться из-за изменений
это было похоже на невинный рефакторинг.
Хотя хорошая архитектура, слабая связь между компонентами может
помочь бороться с этим явлением; ты будешь спать лучше
ночью, если вы написали несколько тестов, чтобы убедиться,
что ничто не нарушит поведение вашей программы.
ОДНАКО,
Никто не может отрицать накладные расходы на написание тестовых случаев. в
В первом случае можно утверждать, что контрольный пример на самом деле руководит
развитие и, следовательно, не должны рассматриваться как накладные расходы.
Честно говоря, я довольно молодой программист, и если бы я был
Вы, мое слово на эту тему не очень ценно ...
Во всяком случае, я думаю, что большинство компаний / проектов не работают
и что юнит-тесты в основном используются во втором
случай ... * * 1023
Другими словами, вместо того, чтобы гарантировать, что программа
работает правильно, он стремится проверить, что он будет
работать так же в будущем.
Эту потребность можно удовлетворить без затрат на написание тестов,
с помощью этого морозильного декоратора.
Допустим, у вас есть функция
def pow(n,k):
if n == 0: return 1
else: return n * pow(n,k-1)
Это прекрасно, и вы хотите переписать его как оптимизированную версию.
Это часть большого проекта. Вы хотите, чтобы он дал тот же результат
за несколько ценностей.
Вместо того, чтобы испытывать боль тестовых случаев, можно использовать некоторые
вид заморозки декоратора.
Что-то такое, что при первом запуске декоратора
декоратор запускает функцию с заданными аргументами (ниже 0 и 7)
и сохраняет результат на карте (f -> args -> result)
@freeze(2,0)
@freeze(1,3)
@freeze(3,5)
@freeze(0,0)
def pow(n,k):
if n == 0: return 1
else: return n * pow(n,k-1)
В следующий раз, когда программа будет выполнена, декоратор загрузит эту карту и проверит
что результат этой функции для этих аргументов не изменился.
Я уже написал быстро декоратор (см. Ниже), но обидел несколько проблем о
который мне нужен твой совет ...
from __future__ import with_statement
from collections import defaultdict
from types import GeneratorType
import cPickle
def __id_from_function(f):
return ".".join([f.__module__, f.__name__])
def generator_firsts(g, N=100):
try:
if N==0:
return []
else:
return [g.next()] + generator_firsts(g, N-1)
except StopIteration :
return []
def __post_process(v):
specialized_postprocess = [
(GeneratorType, generator_firsts),
(Exception, str),
]
try:
val_mro = v.__class__.mro()
for ( ancestor, specialized ) in specialized_postprocess:
if ancestor in val_mro:
return specialized(v)
raise ""
except:
print "Cannot accept this as a value"
return None
def __eval_function(f):
def aux(args, kargs):
try:
return ( True, __post_process( f(*args, **kargs) ) )
except Exception, e:
return ( False, __post_process(e) )
return aux
def __compare_behavior(f, past_records):
for (args, kargs, result) in past_records:
assert __eval_function(f)(args,kargs) == result
def __record_behavior(f, past_records, args, kargs):
registered_args = [ (a, k) for (a, k, r) in past_records ]
if (args, kargs) not in registered_args:
res = __eval_function(f)(args, kargs)
past_records.append( (args, kargs, res) )
def __open_frz():
try:
with open(".frz", "r") as __open_frz:
return cPickle.load(__open_frz)
except:
return defaultdict(list)
def __save_frz(past_records):
with open(".frz", "w") as __open_frz:
return cPickle.dump(past_records, __open_frz)
def freeze_behavior(*args, **kvargs):
def freeze_decorator(f):
past_records = __open_frz()
f_id = __id_from_function(f)
f_past_records = past_records[f_id]
__compare_behavior(f, f_past_records)
__record_behavior(f, f_past_records, args, kvargs)
__save_frz(past_records)
return f
return freeze_decorator
Сброс и сравнение результатов не является тривиальным для всех типов. Сейчас я думаю об использовании функции (я называю это постпроцессом) для решения этой проблемы.
По сути, вместо сохранения res я сохраняю postprocess (res) и сравниваю postprocess (res1) == postprocess (res2) вместо сравнения res1 res2.
Важно позволить пользователю перегружать предопределенную функцию постобработки.
Мой первый вопрос:
Вы знаете способ проверить, является ли объект дампируемым или нет?
Определение ключа для оформленной функции - это боль. В следующих фрагментах
Я использую функциональный модуль и его имя.
** Можете ли вы придумать более разумный способ сделать это. **
Фрагменты, приведенные ниже, работают, но открывают и закрывают файл во время тестирования и записи. Это просто глупый прототип ... но знаете ли вы хороший способ открыть файл, обработать декоратор для всех функций, закрыть файл ...
Я намерен добавить некоторые функции к этому. Например, добавьте возможность определить
итерация для просмотра набора аргументов, записи аргументов из реального использования и т. д.
Почему вы ожидаете от такого декоратора?
В общем, вы бы использовали эту функцию, зная ее ограничение ... Особенно, когда пытаетесь использовать ее с POO?