2012-05-16 5 views
9

Имеется ли в стандартной библиотеке Python ярлык для написания декораторов, которые принимают аргументы?Python: ярлык для написания декораторов, которые принимают аргументы?

Например, если я хочу написать декоратор как with_timeout(timeout):

@with_timeout(10.0) 
def cook_eggs(eggs): 
    while not eggs.are_done(): 
     eggs.cook() 

Я должен написать что-то вроде:

def with_timeout(timeout): 
    _func = [None] 
    def with_timeout_helper(*args, **kwargs): 
     with Timeout(timeout): 
      return _func[0](*args, **kwargs) 
    def with_timeout_return(f): 
     return functools.wraps(f)(with_timeout_helper) 
    return with_timeout_return 

Но это ужасно многословным. Есть ли ярлык, который делает декораторы, которые принимают аргументы, легче писать?

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

Например, возможно, что-то вроде @decorator_with_arguments функции:

@decorator_with_arguments 
def timeout(f, timeout): 
    @functools.wraps(f) 
    def timeout_helper(*args, **kwargs): 
     with Timeout(timeout): 
      return f(*args, **kwargs) 
    return timeout_helper 
+0

Если вам нужна дополнительная помощь декораторы и аннотации см. мой блог. http://blog.mattalcock.com/2013/1/5/decorates-and-annotations/ –

ответ

7

я склонен писать свои декораторы, как классы, чтобы быть честным

class TestWithArgs(object): 
    def __init__(self, *deco_args, **deco_kwargs): 
     self.deco_args = deco_args 
     self.deco_kwargs = deco_kwargs 
    def __call__(self, func): 
     def _wrap(self, *args, **kwargs): 
      print "Blah blah blah" 
      return func(*args, **kwargs) 
     return _wrap 

Его ничего, если не немного понятнее

4

I знаете, вы сказали, что он чувствует себя субоптимальным, но я все еще чувствую, что использование трех вложенных моделей - это самое чистое решение. Внутренние две функции - это просто «нормальный» способ определения декоратора для функции, которая принимает аргументы (см. example в документах python для @wraps). Внешняя - это просто функция, которая принимает и аргументирует и возвращает декоратор.

def with_timeout(timeout): 
    def decorator(f): 
     @wraps(f) 
     def wrapper(*args, **kwargs): 
      with Timeout(timeout): 
       return f(*args, **kwargs) 
     return wrapper 
    return decorator 
+0

Извините, но я не согласен. Нет ничего чистого в трех вложенных функциях (или, если на то пошло, как я это делаю). Это взломать. Лучше будет, например, декоратор '@ decorator_with_arguments' (см. Мой обновленный вопрос). –

4

На основании предложения Якоба, я реализовал небольшой Decorator класс, который я чувствую это довольно приличную работу:

class Decorator(object): 
    def __call__(self, f): 
     self.f = f 
     return functools.wraps(f)(lambda *a, **kw: self.wrap(*a, **kw)) 

    def wrap(self, *args, **kwrags): 
     raise NotImplemented("Subclasses of Decorator must implement 'wrap'") 

class with_timeout(Decorator): 
    def __init__(self, timeout): 
     self.timeout = timeout 

    def wrap(self, *args, **kwargs): 
     with Timeout(timeout): 
      return self.f(*args, **kwargs) 
+1

Я установил опечатку на ваш метод 'with_timeout'' warp' –

+0

D'oh! Благодарю. Вероятно, в 8-й раз я набрал «warp» вместо «wrap» сегодня. И я не набирал «обертывание» много раз. –

0

Во-первых, мы можем определить немного мета-декоратор:

def decorator_with_arguments(wrapper): 
    return lambda *args, **kwargs: lambda func: wrapper(func, *args, **kwargs) 

Это позволяет нам создавать декораторов, которые принимают аргументы, как так:

@decorator_with_arguments 
def my_wrapper(func, *decorator_args, **decorator_kwargs): 
    def wrapped(*call_args, **call_kwargs): 
     print "from decorator:", decorator_args, decorator_kwargs 
     func(*call_args, **call_kwargs) 
    return wrapped 

, который затем может быть использован в обычном режиме:

@my_wrapper(1, 2, 3) 
def test(*args, **kwargs): 
    print "passed directly:", args, kwargs 

test(4, 5, 6) 

Добавление functools.wraps украшение осталось как упражнение :)

0

Другой принять, без использования лямбды:

def decorator_with_arguments(f): 
    @functools.wraps(f) 
    def with_arguments_helper(*args, **kwargs): 
     def decorator(g): 
      return f(g, *args, **kwargs) 
     return decorator 
    return with_arguments_helper