虽然我喜欢把自己想象成一个相当称职的Python编码器,但我从来没有能够理解的语言的一个方面就是装饰器.
我知道它们是什么(表面上),我已经阅读了有关Stack Overflow的教程,示例和问题,我理解语法,可以编写自己的,偶尔使用@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): ...
我用它们进行同步.
import functools def synchronized(lock): """ Synchronization decorator """ def wrap(f): @functools.wraps(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
(或multiprocessing.Lock
自2.6版本)对象结合使用, 以简化装饰器的实现:
import functools def synchronized(lock): """ Synchronization decorator """ def wrap(f): @functools.wraps(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方法.因此,不是重复相同的参数计数,而是一次又一次地异常提升mumbo-jumbo
def myMethod(ID, name): if not (myIsType(ID, 'uint') and myIsType(name, 'utf8string')): raise BlaBlaException() ...
我只是宣布
@accepts(uint, utf8string) def myMethod(ID, name): ...
和accepts()为我做所有的工作.
装饰器用于任何您希望透明地"包装"其他功能的东西.
Django使用它们在视图函数上包装"需要登录"功能,以及注册过滤器函数.
您可以使用类装饰器将命名日志添加到类中.
任何足够通用的功能,你可以"适应"现有的类或功能的行为是公平的装饰游戏.
还讨论了PEP 318指向的Python-Dev新闻组的用例- 函数和方法的装饰器.
对于nosetests,您可以编写一个装饰器,它提供具有多组参数的单元测试函数或方法:
@parameters( (2, 4, 6), (5, 6, 11), ) def test_add(a, b, expected): assert a + b == expected
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()
我主要用它们来调试(打印一个打印其参数和结果的函数的包装)和验证(例如检查一个参数是否是正确的类型,或者在Web应用程序的情况下,如果用户有足够的权限调用一个特定的方法).
我使用以下装饰器来创建函数threadsafe.它使代码更具可读性.它几乎与John Fouhy提出的类似,但不同之处在于,它只能处理单个函数,并且不需要明确地创建锁定对象.
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
装饰器既可用于定义函数的属性,也可用作修改函数的样板; 这是可能的,但反直觉的是他们返回完全不同的功能.看看这里的其他响应,似乎最常见的用途之一是限制其他一些进程的范围 - 无论是日志记录,分析,安全检查等.
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
,把它放在我需要的地方.