jak zrobić dekorator warunkowy w Pythonie 2.6

Czy można warunkowo dekorować funkcję. Na przykład, chcę ozdobić funkcję foo() funkcją timera (timeit) tylko doing_performance_analysis jest True (patrz kod psuedo poniżej).

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)  
Author: Martijn Pieters, 2012-05-23

5 answers

Dekoratory to po prostu dekoratory, które zwracają zamiennik, opcjonalnie tę samą funkcję, owijkę lub coś zupełnie innego. W związku z tym można utworzyć dekorator warunkowy:

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

Teraz możesz go używać tak:

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

Dekoratorem może być również Klasa:

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)

Tutaj metoda __call__ odgrywa taką samą rolę jak zwracana funkcja zagnieżdżona decorator() w pierwszym przykładzie, a parametry dec i condition są tutaj przechowywane jako argumenty na instancja do momentu zastosowania dekoratora.

 25
Author: Martijn Pieters,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-27 13:06:21

Dekorator jest po prostu funkcją zastosowaną do innej funkcji. Można go zastosować ręcznie:

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

if doing_performance_analysis:
    foo = timeit(foo)
 9
Author: Blckknght,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-23 17:23:07

A może:

def foo():
   ...

if doing_performance_analysis:
   foo = timeit(foo)

Wyobrażam sobie, że można nawet zawinąć to do dekoratora, który wziąłby flagę logiczną i inny dekorator, i zastosowałby ten ostatni tylko wtedy, gdy flaga jest ustawiona na 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():
   ...
 4
Author: NPE,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-23 17:27:10

ODPOWIEDŹ Blckknghta jest świetna, jeśli chcesz sprawdzić za każdym razem, gdy wywołujesz funkcję, ale jeśli masz ustawienie, które możesz przeczytać raz i nigdy się nie zmieni, możesz nie chcieć sprawdzać Ustawienia za każdym razem, gdy wywoływana jest funkcja. W niektórych naszych wysokowydajnych demonach w pracy napisałem dekorator, który sprawdza plik ustawień raz, gdy plik Pythona jest ładowany po raz pierwszy i decyduje, czy powinien go zawijać, czy nie.

Oto przykład

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

Zakładając, że log_function_call rejestruje połączenie do bazy danych, pliku dziennika lub cokolwiek innego i tego config.get ('RUN_TIMED_FUNCTIONS') sprawdza twoją globalną konfigurację, a następnie dodanie dekoratora @ timed do funkcji sprawdzi raz przy ładowaniu, aby sprawdzić, czy masz czas na tym serwerze, środowisku itp. a jeśli nie, to nie zmieni to wykonania funkcji na produkcji lub w innych środowiskach, w których zależy ci na wydajności.

 0
Author: nobled,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-16 22:15:11
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()
 0
Author: Lawrence Chernin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-17 00:10:41