Вопрос по python – как сделать условный декоратор в Python

20

Можно ли оформить функцию условно. Например, я хочу украсить функциюfoo() с функцией таймера (timeit) onlyying_performance_analysis естьTrue (см. псевдо-код ниже).

if doing_performance_analysis:
  @timeit
  def foo():
    """
    do something, timeit function will return the time it takes
    """
    time.sleep(2)
else:
  def foo():
    time.sleep(2)  

Ваш Ответ

6   ответов
0

Вот что сработало для меня:

def timeit(method):
    def timed(*args, **kw):
        if 'usetimer' not in kw:
            return method(*args, **kw)
        elif ('usetimer' in kw and kw.get('usetimer') is None):
            return method(*args, **kw)
        else:
            import time
            ts = time.time()
   ,         result = method(*args, **kw)
            te = time.time()
            if 'log_time' in kw:
                name = kw.get('log_name', method.__name__.upper())
                kw['log_time'][name] = int((te - ts) * 1000)
            else:
                print '%r took %2.2f ms' % \
                      (method.__name__, (te - ts) * 1000)
            return result
    return timed

def some_func(arg1, **kwargs):
    #do something here

some_func(param1, **{'usetimer': args.usetimer})
4

Как насчет:

def foo():
   ...

if doing_performance_analysis:
   foo = timeit(foo)

Я полагаю, что вы могли бы даже обернуть это в декоратор, который будет принимать логический флаг и другой декоратор, и применять его только в том случае, если флаг установлен вTrue:

def cond_decorator(flag, dec):
   def decorate(fn):
      return dec(fn) if flag else fn
   return decorate

@cond_decorator(doing_performance_analysis, timeit)
def foo():
   ...
0
use_decorator = False

class myDecorator(object):
    def __init__(self, f):
            self.f = f

    def __call__(self):
            print "Decorated running..."
            print "Entering", self.f.__name__
            self.f()
            print "Exited", self.f.__name__


def null(a):
    return a


if use_decorator == False :
    myDecorator = null


@myDecorator
def CoreFunction():
    print "Core Function running"

CoreFunction()
9

Декоратор - это просто функция, примененная к другой функции. Вы можете применить его вручную:

def foo():
   # whatever
   time.sleep(2)

if doing_performance_analysis:
    foo = timeit(foo)
0

Ответ Blckknght хорош, если вы хотите выполнять проверку каждый раз, когда вызываете функцию, но если у вас есть настройка, которую вы можете прочитать один раз и никогда не изменять, вы можете не захотеть проверять настройку каждый раз, когда вызывается украшенная функция. В некоторых из наших высокопроизводительных работающих демонов я написал декоратор, который проверяет файл настроек один раз при первой загрузке файла python и решает, следует ли ему обернуть его или нет.

Вот образец

def timed(f):
    def wrapper(*args, **kwargs):
        start = datetime.datetime.utcnow()
        return_value = f(*args, **kwargs)
        end = datetime.datetime.utcnow()
        duration = end - start

        log_function_call(module=f.__module__, function=f.__name__, start=__start__, end=__end__, duration=duration.total_seconds())
    if config.get('RUN_TIMED_FUNCTIONS'):
        return wrapper
    return f

Предполагая, что log_function_call записывает ваш вызов в базу данных, файл журнала или что-то еще, и этот config.get (& reg; & gt; RUN_TIMED_FUNCTIONS & apos;) проверяет вашу глобальную конфигурацию, затем добавление декоратора @timed в функцию будет проверять один раз при загрузке, чтобы увидеть, синхронизируете ли вы время на этом сервере, в среде и т. д., а если нет, то это не изменит выполнение функции на производстве или в других средах, где вы заботитесь о производительности.

31

Декораторы - это просто вызываемые объекты, которые возвращают замену, необязательно ту же функцию, обертку или что-то совершенно другое. Таким образом, вы можете создать условный декоратор:

def conditional_decorator(dec, condition):
    def decorator(func):
        if not condition:
            # Return the function unchanged, not decorated.
            return func
        return dec(func)
    return decorator

Теперь вы можете использовать это так:

@conditional_decorator(timeit, doing_performance_analysis)
def foo():
    time.sleep(2)  

Декоратором также может быть класс:

class conditional_decorator(object):
    def __init__(self, dec, condition):
        self.decorator = dec
        self.condition = condition

    def __call__(self, func):
        if not self.condition:
            # Return the function unchanged, not decorated.
            return func
        return self.decorator(func)

Здесь__call__ метод играет ту же роль, что и возвращаемыйdecorator() вложенная функция в первом примере, и закрытаяdec а такжеcondition параметры здесь сохраняются в качестве аргументов в экземпляре, пока не будет применен декоратор.

@PythonEnthusiast: просто передав результат вызова:@method_decorator(conditional_decorator(timeit, doing_performance_analysis)).
Спасибо! Раздел комментариев не форматируется, поэтому я добавил пример кода в исходный ответ. Можете ли вы объяснить, почему функция синхронизации не вызывается? cfpete
Как бы я использовал это, если я хочу использовать это на основанных на классе методах, то есть представлениях на основе класса Django. Там мы должны использоватьmethod_decorator, Как сделать этот код совместимым с этим?
Декоратор применяется во время импорта, поэтому с переменными экземпляра в это время не обращаются. Для этого вам нужно написать другой декоратор, который будет проверять себя при вызове. Вне области для этого формата Q и комментариев. :-)

Похожие вопросы