装饰器的用法和用例
- 参数检查装饰器
- 缓存装饰器
- 代理装饰器
- 上下文装饰器
参数检查(检查被装饰函数的输入输出参数类型)
- XML-RPC:轻量级的远程过程调用协议,该协议提供了扩展,可以用来发现服务器的API,Python的xmlrpc模块实现了这个扩展。
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, type_ = couple
if isinstance(arg, type_):
continue
raise TypeError('arg %d should be %s'%(index, type_))
def __xmlrpc(*args):
checkable_args = args[1:]
_check_types(checkable_args, in_)
result = function(*args)
if not type(result) in (tuple, list):
checkable_res = (result, )
else :
checkable_res = result
_check_types(checkable_res, out)
return result
return __xmlrpc
return _xmlrpc
class RPCView:
@xmlrpc((int, int))
def function_a(self, int1, int2):
print('received %d and %d'%(int1, int2))
@xmlrpc((int, int), (float, float))
def function_b(self, int1, int2):
print('received %d and %d'%(int1, int2))
print('print return %f and %f'%(int1/2.0, int2/3.0))
return (int1/2., int2/3.0)
>>>rpc_info
{'function_a': ((int, int), (NoneType,)),
'function_b': ((int, int), (float, float))}
>>>demo = RPCView()
>>>demo.function_a(1, 2)
received 1 and 2
>>>demo.function_a(1.0, 2)
TypeError Traceback (most recent call last)
----> 1 demo.function_a(1.0, 2)
---> 22 _check_types(checkable_args, in_)
---> 17 raise TypeError('arg %d should be %s'%(index, type_))
TypeError: arg 0 should be <class 'int'>
>>>demo.function_b(1, 2)
received 1 and 2
print return 0.500000 and 0.666667
(0.5, 0.6666666666666666)
缓存
- 与参数类型检查装饰器类似,但是重点关注内部状态不影响输出的函数
- 可以把输出和计算它所需的参数放在一起,并在后续的调用中直接返回(memoizing)
- 缓存计算代价较高的函数可以显著提高程序的性能,缓存也可以和函数本身绑定管理其作用域和周期
- 推荐已经实现高级缓存算法的缓存库
import time
import pickle
import hashlib
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, **kwargs):
key = compute_key(function, args, kwargs)
if (key in cache and is_obsolete(cache[key], duration)):
print('This function is no more than 10s from the last call.')
print('So , we got the value in cache !!!')
return cache[key]['value']
else :
result = function(*args, **kwargs)
print('The cache has been refreshed and needs to be recalculated')
cache[key] = {'value' : result, 'time':time.time()}
return result
return __memoize
return _memoize
@memoize(10)
def a_very_complex_funcion(a, b):
return a+b
>>>a_very_complex_funcion(1, 2)
The cache has been refreshed and needs to be recalculated
3
>>>a_very_complex_funcion(2, 3)
The cache has been refreshed and needs to be recalculated
5
>>>a_very_complex_funcion(3, 4)
The cache has been refreshed and needs to be recalculated
7
>>>cache
{'2fc4d64416bfd19e3ee50db24c6705b8ed027c2e': {'value': 3,
'time': 1560933500.7079651},
'e169985e967d82eebb70cb1a3c96ebf2788e559a': {'value': 5,
'time': 1560933501.8255372},
'a6aaf959c612d819d4edfd3e91a306698708b670': {'value': 7,
'time': 1560933502.7970798}}
>>>a_very_complex_funcion(3, 4)
This function is no more than 10s from the last call.
So , we got the value in cache !!!
7
代理
- 代理装饰器使用全局机制来标记和注册函数
- 应用场景:web应用中,代理装饰器用来检查用户的角色和权限
class User(object):
def __init__(self, roles):
self.roles = roles
class Unauthorized(Exception):
pass
def protect(role):
def _protect(function):
def __protect(*args, **kwargs):
user = globals().get('user')
if user is None or role not in user.roles:
raise Unauthorized('Permission denied')
return function(*args, **kwargs)
return __protect
return _protect
tarek = User(('黄飞鸿','霍元甲','陈真','强森'))
bill = User(('卖鱼强',))
class MySecrets(object):
@protect('霍元甲')
def waffle_recipe(self):
print('you can do that')
user = tarek
secret = MySecrets()
secret.waffle_recipe()
you can do that
user = User(('奥特曼','凹凸曼'))
secret.waffle_recipe()
---------------------------------------------------------------------------
Unauthorized Traceback (most recent call last)
in
1 user = User(('奥特曼','凹凸曼'))
----> 2 secret.waffle_recipe()
in __protect(*args, **kwargs)
11 user = globals().get('user') # globals()返回一个包含全局变量的字典,变量名的字符串为键值
12 if user is None or role not in user.roles:
---> 13 raise Unauthorized('Permission denied')
14 return function(*args, **kwargs)
15 return __protect
Unauthorized: Permission denied
上下文
- 确保函数运行在正确的上下文中
- 应用场景:比如一个数据项需要在多个线程之间共享,需要一个锁来保护他,避免多次访问,上下文装饰器可以实现这锁。
- 上下文管理器with可以取代上下文装饰器
from threading import RLock
lock = RLock()
def synchronized(function):
def _synchronized(*args, **kwargs):
lock.acquire()
try :
return function(*args, **kwargs)
finally:
lock.release()
return _synchronized
@synchronized
def thread_safe():
pass