虽然我喜欢认为自己是一个相当称职的Python程序员,但我始终无法理解该语言的一个方面是装饰器。

我知道它们是什么(表面上),我读过教程,例子,关于Stack Overflow的问题,我理解语法,可以自己写,偶尔使用@classmethod和@staticmethod,但我从来没有想过在我自己的Python代码中使用装饰器来解决问题。我从来没有遇到过这样的问题:“嗯……这看起来像是装修师的工作!”

所以,我想知道你们是否可以提供一些在你们自己的程序中使用装饰器的例子,希望我能有一个“啊哈!”的时刻,并得到它们。


当前回答

装饰器既可以用来定义函数的属性,也可以用作修改函数的样板;它们可能返回完全不同的函数,但这是违反直觉的。看看这里的其他响应,最常见的用途之一似乎是限制一些其他进程的范围——无论是日志记录、概要分析、安全检查等等。

CherryPy使用对象调度将url匹配到对象,并最终匹配到方法。这些方法上的装饰符表示是否允许CherryPy使用这些方法。例如,改编自教程:

class HelloWorld:

    ...

    def secret(self):
        return "You shouldn't be here."

    @cherrypy.expose
    def index(self):
        return "Hello world!"

cherrypy.quickstart(HelloWorld())

其他回答

我最近在开发社交网络web应用程序时使用了它们。对于社区/组,我应该给予成员权限来创建新的讨论和回复消息,您必须是该特定组的成员。所以,我写了一个装饰器@membership_required,并把它放在视图中我需要的地方。

装饰器既可以用来定义函数的属性,也可以用作修改函数的样板;它们可能返回完全不同的函数,但这是违反直觉的。看看这里的其他响应,最常见的用途之一似乎是限制一些其他进程的范围——无论是日志记录、概要分析、安全检查等等。

CherryPy使用对象调度将url匹配到对象,并最终匹配到方法。这些方法上的装饰符表示是否允许CherryPy使用这些方法。例如,改编自教程:

class HelloWorld:

    ...

    def secret(self):
        return "You shouldn't be here."

    @cherrypy.expose
    def index(self):
        return "Hello world!"

cherrypy.quickstart(HelloWorld())

装饰器用于您希望透明地“包装”附加功能的任何内容。

Django使用它们在视图函数中包装“登录必需”的功能,以及注册过滤器函数。

可以使用类装饰器将命名日志添加到类中。

任何可以“附加”到现有类或函数行为上的足够通用的功能都可以用作装饰。

在Python-Dev新闻组中也有关于用例的讨论,PEP 318—函数和方法的装饰器。

Twisted库将装饰器与生成器结合使用,让人误以为异步函数是同步的。例如:

@inlineCallbacks
def asyncf():
    doStuff()
    yield someAsynchronousCall()
    doStuff()
    yield someAsynchronousCall()
    doStuff()

使用这种方法,原本被分解成大量小回调函数的代码可以很自然地编写为一个块,使其更容易理解和维护。

一个明显的用途当然是日志记录:

import functools

def log(logger, level='info'):
    def log_decorator(fn):
        @functools.wraps(fn)
        def wrapper(*a, **kwa):
            getattr(logger, level)(fn.__name__)
            return fn(*a, **kwa)
        return wrapper
    return log_decorator

# later that day ...
@log(logging.getLogger('main'), level='warning')
def potentially_dangerous_function(times):
    for _ in xrange(times): rockets.get_rocket(NUCLEAR=True).fire()