语句装饰器

Statement decorators

我们有一些代码如下所示:

from third_party_library import foo

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

我想使用装饰器,让我们的代码更简洁,看起来像这样:

from third_party_library import foo

@retry(3)
foo(args)

这给出了语法错误。我是不是遗漏了什么,或者 python 只是不允许在语句上使用装饰器?

Python 不允许在语句上使用装饰器;它们只允许在 class 和函数定义中使用。您可以在 the grammar specification.

的顶部附近看到它

装饰器只能应用于函数和class 定义,例如:

@decorator
def func():
    ...

@decorator
class MyClass(object):
    ...

您不能在该语言的其他任何地方使用它们。


要执行您想要的操作,您可以制作一个普通的 retry 函数并将 fooargs 作为参数传递。实现看起来像这样:

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)

装饰器是在 Python 2.4 中引入的。最初,它们仅支持函数和方法声明 (PEP 318)。

Python 3 将它们扩展为 class 声明 (PEP 3129).

任何其他语言构造都不支持装饰器。

装饰器确实不能应用于语句,你可以按照另一个答案中描述的重试功能。然而,在我看来,还有一种更像 Python 的方法,即使用上下文管理器。让我以可运行的方式重新表述您的问题,让代码说话:

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 and https://docs.python.org/2/reference/datamodel.html#context-managers

装饰器本身就是包装函数(以参数为函数)和return新函数的函数;

因此您可以像这样内联使用它:

retry(3)(foo)(args)
#retry(3) is function accept function
#retry(3)(foo) return the new function 
#then u exploit the function by call with the `args`