由于装饰器在模块被首次读取时由解释器来加载,所以它们的使用应受限于通用的包装器(wrapper)。如果装饰器与方法的类或所增强的函数签名绑定,那么应该将其重构为
常规的可调用对象,以避免复杂性。在任何情况下,装饰器在处理 API 时,一个好的做法
是将它们聚集在一个易于维护的模块中。
常见的装饰器模式如下所示。
• 参数检查。
• 缓存。
• 代理。
• 上下文提供者。
参数检查
检查函数接受或返回的参数,在特定上下文中执行时可能有用。举个例子,如果一个
函数要通过 XML-RPC 来调用,那么 Python 无法像静态语言那样直接提供其完整签名。当
XML-RPC 客户端请求函数签名时,就需要用这个功能来提供内省能力。
自定义装饰器可以提供这种类型的签名,并确保输入和输出代表自定义的签名参数:
rpc_info = {}
def xmlrpc(in_=(), out=(type(None),)):
def _xmlrpc(function):
注册签名
func_name = function.name
rpc_info[func_name] = (in_, out)
def _check_types(elements, 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))
包装过的函数
def __xmlrpc(*args): # 没有允许的关键词
检查输入的内容
checkable_args = args[1:] # 去掉 self
check_types(checkable_args, in)
运行函数
res = function(*args)
检查输出的内容
if not type(res) in (tuple, list):
checkable_res = (res,)
else:
checkable_res = res
_check_types(checkable_res, out)
函数及其类型检查成功
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_infos 字典,并用于检查参数类型的特定环境中:
rpc_ _info
{‘meth2’: ((<class ‘str’>,), (<class ‘int’>,)), ‘meth1’: ((<class
‘int’>, <class ‘int’>), (<class ‘NoneType’>,))}
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 <class ‘str’>