Python装饰器的一些常见用途是什么?

虽然我喜欢把自己看作是一个合理的Python编码器,但是我从来没有想到的语言的一个方面是装饰器。

我知道他们是什么(表面上),我读过教程,例子,关于堆栈溢出的问题,并且我理解语法,可以写我自己的,偶尔使用@classmethod和@staticmethod,但是从来没有发生过使用装饰者来解决我自己的Python代码中的一个问题。 我从来没有遇到过这样的问题:“呃…这看起来像是一个装饰工的工作!”

所以,我想知道你们是否可以提供一些你在自己的程序中使用过装饰器的例子,希望我会有一个“A-ha”! 一瞬间,并得到他们。

我使用装饰器主要用于计时目的

def time_dec(func): def wrapper(*arg): t = time.clock() res = func(*arg) print func.func_name, time.clock()-t return res return wrapper @time_dec def myFunction(n): ... 

我用它们进行同步。

 def synchronized(lock): """ Synchronization decorator """ def wrap(f): def newFunction(*args, **kw): lock.acquire() try: return f(*args, **kw) finally: lock.release() return newFunction return wrap 

正如在注释中指出的那样,由于Python 2.5可以使用with语句和threading.Lock (或2.6版以后的multiprocessing.Lock )对象来简化装饰器的实现,只需:

 def synchronized(lock): """ Synchronization decorator """ def wrap(f): def newFunction(*args, **kw): with lock: return f(*args, **kw) return newFunction return wrap 

无论如何,你这样使用它:

 import threading lock = threading.Lock() @synchronized(lock) def do_something(): # etc @synchronzied(lock) def do_something_else(): # etc 

基本上它只是在函数调用的任一侧放置lock.acquire() / lock.release()

我使用装饰器来检查通过一些RMI传递给我的Python方法的参数。 因此,不要重复相同的参数计数,反复地重复例外

 def myMethod(ID, name): if not (myIsType(ID, 'uint') and myIsType(name, 'utf8string')): raise BlaBlaException() ... 

我只是申报

 @accepts(uint, utf8string) def myMethod(ID, name): ... 

并接受()为我做所有的工作。

装饰器用于任何你想透明地“包装”附加function的东西。

Django使用它们在视图函数上包装“需要login”function ,以及注册filter函数 。

您可以使用类装饰器来向类中添加指定的日志 。

任何足够通用的function,你可以“贴”到一个现有的类或function的行为是公平的装饰游戏。

还讨论了PEP 318指向Python-Dev新闻组的用例 – 装饰器的函数和方法 。

Twisted库使用与生成器相结合的装饰器来给出错觉,即asynchronous函数是同步的。 例如:

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

使用这种方式,将被分解成许多小callback函数的代码可以很自然地作为一个单独的块来编写,这使得理解和维护起来更容易。

对于nosetests,你可以写一个装饰器,提供一个unit testing函数或方法的几组参数:

 @parameters( (2, 4, 6), (5, 6, 11), ) def test_add(a, b, expected): assert a + b == expected 

Python Wiki中有许多build议的用法和片段。

我主要使用它们来进行debugging(封装一个打印参数和结果的函数)和validation(例如,检查参数是否是正确的types,或者在Web应用程序中,如果用户有足够的权限调用特定的方法)。

其实我最近有一个“阿哈!” 时刻,当你打电话给他们,并使用一个装饰器,使我只能剖析装饰的function/方法。 这是该文件中的profile_func修饰器,其输出可以在KCacheGrind中查看。 确实非常有用。

我正在使用下面的装饰器来创build函数线程安全。 它使代码更具可读性。 它与John Fouhy提出的几乎相似,区别在于一个函数在单个函数上工作,并且不需要明确地创build一个锁对象。

 def threadsafe_function(fn): """decorator making sure that the decorated function is thread safe""" lock = threading.Lock() def new(*args, **kwargs): lock.acquire() try: r = fn(*args, **kwargs) except Exception as e: raise e finally: lock.release() return r return new class X: var = 0 @threadsafe_function def inc_var(self): X.var += 1 return X.var 

当然,一个明显的用途是logging日志。

 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() 

装饰器既可以用来定义一个函数的属性,也可以用来改变它的样板。 对于他们来说完全不同的function是可能的,但却是违反直觉的。 看看这里的其他答案,似乎最常见的用法之一是限制其他进程的范围 – 无论是日志logging,分析,安全检查等。

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()) 

我最近在使用社交networking应用程序时使用了它们。 对于社区/团体,我应该给予会员授权以创build新的讨论并回复您必须成为该特定团体成员的消息。 所以,我写了一个装饰器@membership_required ,把我认为需要的地方。

我使用这个装饰器来修复参数

 def fill_it(arg): if isinstance(arg, int): return "wan" + str(arg) else: try: # number present as string if str(int(arg)) == arg: return "wan" + arg else: # This should never happened raise Exception("I dont know this " + arg) print "What arg?" except ValueError, e: return arg def fill_wanname(func): def wrapper(arg): filled = fill_it(arg) return func(filled) return wrapper @fill_wanname def get_iface_of(wanname): global __iface_config__ return __iface_config__[wanname]['iface'] 

这写我重构一些函数需要传递参数“wanN”,但在我的旧代码,我只通过N或'N'

装饰器可以用来轻松创build函数方法variables。

 def static_var(varname, value): ''' Decorator to create a static variable for the specified function @param varname: static variable name @param value: initial value for the variable ''' def decorate(func): setattr(func, varname, value) return func return decorate @static_var("count", 0) def mainCallCount(): mainCallCount.count += 1