2015-01-30 7 views
2

У нас есть некоторый код, который выглядит следующим образом:Заявление декораторы

from third_party_library import foo 

for n in range(3): 
    try: 
     foo(args) 
     break 
    except: 
     print "Retry %i/3" % n 

Я хотел бы использовать декоратор, позволяя наш код более consise, глядя, как это:

from third_party_library import foo 

@retry(3) 
foo(args) 

Это дает синтаксическая ошибка. Я что-то упустил, или python просто не разрешает декораторам на операторах?

ответ

7

Декораторы может быть применен только к функции и классу определения, такие как:

@decorator 
def func(): 
    ... 

@decorator 
class MyClass(object): 
    ... 

Вы не можете использовать их в другом месте на языке.


Чтобы сделать то, что вы хотите, вы могли бы сделать нормальную retry функцию и передать foo и args в качестве аргументов. Реализация будет выглядеть примерно так:

def retry(times, func, *args, **kwargs): 
    for n in xrange(times): 
     try: 
      func(*args, **kwargs) 
      break 
     except Exception: # Try to catch something more specific 
      print "Retry %i/%i" % (n, times) 
+0

3 должен быть первым, чтобы '* args' (и' ** kwargs') можно было использовать для поддержки функций с несколькими аргументами. В противном случае именно то, что я собираюсь предложить. – delnan

+0

@ delnan - Хорошая идея. – iCodez

+0

Вы можете использовать декораторы функций inline. Например, если у ОП была гипотетическая декоративная 'retry()', они могли бы вызвать 'retry (3) (foo)()' в своем цикле for. – mipadi

2

Python не допускает декораторов на утверждения; они разрешены только для определений функций класса &. Вы можете видеть это в верхней части the grammar specification.

2

Декораторы были представлены на Python 2.4. Первоначально они поддерживались только для объявлений функций и методов (PEP 318). (PEP 3129).

Декораторы не поддерживаются ни на каких других языках.

0

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

import random 
def foo(): 
    """The function you were importing, now runnable""" 
    n = random.randint(1,10) 
    print "Got", n, 
    if n < 8: 
     raise Exception("Failed") 

for n in range(3): 
    try: 
     foo() 
     break 
    except: 
     print "Retry %i/3" % n 

с использованием (автоматически сгенерированный) ContextManager, код будет выглядеть следующим образом:

import random 
def foo(): 
    """The function you were importing, now runnable""" 
    n = random.randint(1,10) 
    print "Got", n, 
    if n < 8: 
     raise Exception("Failed") 

from contextlib import contextmanager 
@contextmanager 
def retry(): 
    """Something similar to a decorator for statements""" 
     try: 
      yield 
     except: 
      print "Retry %i/3" % n 

for n in range(3): 
    with retry(): 
     foo() 
     break 

Как вы можете видеть, решение не является совершенным, так как ContextManager может выполнять оператор (ы) ровно один раз (на момент выхода), а не в цикле. Однако, по крайней мере, для этого простого примера мне кажется более понятным, хотя цикл должен выполняться снаружи. См. https://docs.python.org/2.7/library/contextlib.html и https://docs.python.org/2/reference/datamodel.html#context-managers для получения более подробной информации.

Смежные вопросы