装饰器常用于以下方面,本文先介绍前面两种:
当函数在某些特别的上下文执行时,对其接收或返回的参数进行检查是很有用的。例如,如果某个函数将通过 XML-RPC 的方式被调用时,Python 将无法像那些静态类型语言一样能直接提供函数的完整签名。该特性需要在XML-RPC 客户端请求函数签名时提供自省能力。
注释:XML-RPC 协议
XML-RPC 是一个轻量级的远程过程调用协议,该协议在 HTTP 上使用 XML 来编码它的调用。在简单的客户端-服务器交互中,常使用该协议,而不使用SOAP。不像 SOAP 会提供一个列出所有可调用函数(WSDL)的页面,XML-RPC 没有可用函数的目录。关于扩展该协议,以允许发现服务器 API 的提议已经被提出,并且 Python 的 xmlrpc 模块已经实现了该功能(参考:https://docs.python.org/3/library/xmlrpc.server.html)
一个定制的装饰器就能够提供这种类型的签名。就所定义的签名参数而言,装饰器还可以确保要输入什么,要得到什么输出。
rpc_info = {}
def xmlrpc(in_=(), out=(type(None),)):
def _xmlrpc(function):
# registering the signature
func_name = function.__name__
rpc_info[func_name] = (in_, out)
def _check_types(elements, types):
"""Subfunction that checks the types."""
if len(elements) != len(types):
raise TypeError('argument count is wrong')
typed = enumerate(zip(elements, types))
for index, couple in typed:
arg, of_the_right_type = couple
if isinstance(arg, of_the_right_type):
continue
raise TypeError('arg #%d should be %s' % (index, of_the_right_type))
# wrapped function
def __xmlrpc(*args): # no keywords allowed
# checking what goes in
checkable_args = args[1:] # removing self
_check_types(checkable_args, in_)
# running the function
res = function(*args)
# checking what goes out
if not type(res) in (tuple, list):
checkable_res = (res,)
else:
checkable_res = res
_check_types(checkable_res, out)
# the function and the type
# checking succeeded
return res
return __xmlrpc
return _xmlrpc
下面是一个使用该装饰器的例子:
class RPCView:
@xmlrpc((int, int)) # two int -> None
def meth1(self, int1, int2):
print('received %d and %d' % (int1, int2))
@xmlrpc((str,), (int,)) # string -> int
def meth2(self, phrase):
print('received %s' % phrase)
return 12
当这个类被读入后,该类的定义就会被存在 rpc_info 这个字典中,这些信息可以在一些需要检查参数类型的特殊环境中使用。
>>> rpc_info
{'meth2': ((,), (,)), 'meth1': ((, ), (,))}
>>> my = RPCView()
>>> my.meth1(1, 2)
received 1 and 2
>>> my.meth2(2)
Traceback (most recent call last):
File "", line 1, in
File "", line 26, in __xmlrpc
File "", line 20, in _check_types
TypeError: arg #0 should be
缓存装饰器和参数检查装饰器很像,但是更关注那些内部状态不会影响输出的函数。对于这样的函数,其每一组参数都可以关联到一个独特的结果。这种风格的编程是函数式编程的特色(函数编程请查看:http://en.wikipedia.org/wiki/Functional_programming),当输入值的集合是有限的时可以使用。
因此,缓存装饰器能够将输出结果和产生这个结果所需要的输入参数绑定起来,这样在随后的相同调用中就可以直接返回结果。这种行为叫做记忆(memoizing,http://en.wikipedia.org/wiki/Memoizing),并且使用装饰器来实现是很简单的:
import time
import hashlib
import pickle
cache = {}
def is_obsolete(entry, duration):
return time.time() - entry['time']> duration
def compute_key(function, args, kw):
key = pickle.dumps((function.__name__, args, kw))
return hashlib.sha1(key).hexdigest()
def memoize(duration=10):
def _memoize(function):
def __memoize(*args, **kw):
key = compute_key(function, args, kw)
# do we have it already ?
if (key in cache and not is_obsolete(cache[key], duration)):
print('we got a winner')
return cache[key]['value']
# computing
result = function(*args, **kw)
# storing the result
cache[key] = {
'value': result,
'time': time.time()
}
return result
return __memoize
return _memoize
下面是使用该装饰器的一个例子:
>>> @memoize()
... def very_very_very_complex_stuff(a, b):
... # if your computer gets too hot on this calculation
... # consider stopping it
... return a + b
...
>>> very_very_very_complex_stuff(2, 2)
4
>>> very_very_very_complex_stuff(2, 2)
we got a winner
4
>>> @memoize(1) # invalidates the cache after 1 second
... def very_very_very_complex_stuff(a, b):
... return a + b
...
>>> very_very_very_complex_stuff(2, 2)
4
>>> very_very_very_complex_stuff(2, 2)
we got a winner
4
>>> cache
{'c2727f43c6e39b3694649ee0883234cf': {'value': 4, 'time': 1199734132.7102251)}
>>> time.sleep(2)
>>> very_very_very_complex_stuff(2, 2)
4
缓存计算代价‘昂贵’的函数,能够显著地提升应用程序的性能,但是使用时要小心。缓存的值也可以被绑定到函数自身,来管理其作用域,生命周期,而不是存放到一个集中的字典中。但是,更高效的缓存装饰器应该使用以高级缓存算法为基础的特殊的缓存库。