从2.2开始,就有了staticmethod和classmethod,但直到2.4以前,还必须这样定义:
def foo(cls, arg):
perform method operation
...
this method definition is
very
very long.
foo = classmethod(foo)
@classmethod
def foo(cls, arg):
perform method operation
...
this method definition is
very
very long.
def onexit(f):
import atexit
atexit.register(f)
return f
@onexit
def func():
...
def attrs(**kwds):
def decorate(f):
for k in kwds:
setattr(f, k, kwds[k])
return f
return decorate
@attrs(versionadded="2.2",
author="Guido van Rossum")
def mymethod(f):
...
def accepts(*types):
def check_accepts(f):
assert len(types) == f.func_code.co_argcount
def new_f(*args, **kwds):
for (a, t) in zip(args, types):
assert isinstance(a, t), \
"arg %r does not match %s" % (a,t)
return f(*args, **kwds)
new_f.func_name = f.func_name
return new_f
return check_accepts
def returns(rtype):
def check_returns(f):
def new_f(*args, **kwds):
result = f(*args, **kwds)
assert isinstance(result, rtype), \
"return value %r does not match %s" % (result,rtype)
return result
new_f.func_name = f.func_name
return new_f
return check_returns
@accepts(int, (int,float))
@returns((int,float))
def func(arg1, arg2):
return arg1 * arg2
解决方法是提供一个通用的方法来构造decorator,以隐藏签名保持所要求的复杂性。 decorator模块就提供了这样一个类,名为 decorator 。 小写名称的意义是一般像函数一样使用它,而不是把它当作类。
定义
技术上讲,任何能以一个参数调用的python对象都可以作为decorator。 但是要做到有用的decorator往往是很大的, 为方便叙述将所有decorator分为两类:
改变特征的decorator有它特殊的用途, classmethod 和 staticmethod 就是这个类型。
特征保留型decorator是可叠回的。相反classmethod或staticmethod是显然不可叠加的。
然而从头写一个特征保留型并不是那样显然的,特别是想为任意特征的函数写合适的decorator时。
陈述问题
假设你想跟踪一个函数,这个decorator的很典型的用途:
#<_main.py>
def decorator_trace(f):
def newf(*args, **kw):
print "calling %s with args %s, %s" % (f.__name__, args, kw)
return f(*args, **kw)
newf.__name__ = f.__name__
newf.__dict__ = f.__dict__
newf.__doc__ = f.__doc__
newf.__module__ = f.__module__
return newf
#</_main.py>
这个实现可以工作在任意特征的函数上,但不幸的是,这个decorator并不是特征保留的,
考虑这个实例:
>>> @decorator_trace ... def f1(x): ... pass
这里原始函数只能接受一个参数x但decorator返回的函数能接受任意多个参数:
>>> from inspect import getargspec >>> print getargspec(f1) ([], 'args', 'kw', None)
探测工具报告说该函数可以接受 *args 和 **kw ,但当你以多于一个参数调用时就会出错:
>>> f1(0, 1) Traceback (most recent call last): ... TypeError: f1() takes exactly 1 argument (2 given)
decorator 工厂的另一个重要的优点在于你可以使用不必使用嵌套函数就能解决问题。 如下面的定义 decorator_trace
>>> from decorator import decorator
首先定义一个helper function,使用的签名是 (f, *args, **kw)
#<_main.py>
def trace(f, *args, **kw):
print "calling %s with args %s, %s" % (f.func_name, args, kw)
return f(*args, **kw)
#</_main.py>
decorator 有能力将它转化为签名保持型的decorator
>>> @decorator(trace) ... def f1(x): ... pass
调用一下来检查其工作正确:
>>> f1(0)
calling f1 with args (0,), {}
再检查签名:
>>> print getargspec(f1) (['x'], None, None, None)
它也同样能工作在任意签名的函数上:
>>> @decorator(trace) ... def f(x, y=1, z=2, *args, **kw): ... pass
>>> f(0, 3)
calling f with args (0, 3, 2), {}
>>> print getargspec(f) (['x', 'y', 'z'], 'args', 'kw', (1, 2))
即使函数包含了外来签名也照样能工作:
>>> @decorator(trace) ... def exotic_signature((x, y)=(1,2)): return x+y
>>> print getargspec(exotic_signature)
([['x', 'y']], None, None, ((1, 2),))
>>> exotic_signature()
calling exotic_signature with args ((1, 2),), {}
3
decorator 也是一个decorator
它也是一个会签名改变型的decorator
示例: memoize
这是一个实现了记忆特征的decorator,也就是它将结果缓存在了一个内部dict中,当下次以同样的参数调用时它直接返回而不用计算。
#<_main.py>
from decorator import decorator
def getattr_(obj, name, default_thunk):
"Similar to .setdefault in dictionaries."
try:
return getattr(obj, name)
except AttributeError:
default = default_thunk()
setattr(obj, name, default)
return default
@decorator
def memoize(func, *args):
dic = getattr_(func, "memoize_dic", dict)
# memoize_dic is created at the first call
if args in dic:
return dic[args]
else:
result = func(*args)
dic[args] = result
return result
#</_main.py>
这是它的测试
>>> @memoize ... def heavy_computation(): ... time.sleep(2) ... return "done"
>>> print heavy_computation() # the first time it will take 2 seconds done
>>> print heavy_computation() # the second time it will be instantaneous done
在多线程的程序中,decorator有很多用途 [1]
| [1] | In Python 2.5, 最好的使用锁定语义是使用with语句 the with statement: http://docs.python.org/dev/lib/with-locks.html |
def delayed(nsec):
def call(proc, *args, **kw):
thread = threading.Timer(nsec, proc, args, kw)
thread.start()
return thread
return decorator(call)
>>> @delayed(2) ... def start_browser(): ... "code to open an external browser window here"
>>> #start_browser() # will open the browser in 2 seconds >>> #server.serve_forever() # enter the server mainloop
threaded = delayed(0) # no-delay decorator
>>> @threaded ... def writedata(data): ... write(data)
资源阻塞
重定向输出
| [2] | Python PEP 0318: http://www.python.org/dev/peps/pep-0318/ |
| [3] | 本文decorator module引用自: http://www.python.org/pypi/decorator/1.1 |
| [4] | http://www.phyast.pitt.edu/~micheles/python/documentation.html |
| [5] | http://www.phyast.pitt.edu/~micheles/python/decorator.zip |
| [6] | http://wiki.python.org/moin/PythonDecoratorLibrary |