装饰器的四个用法

装饰器的用法和用例

  • 参数检查装饰器
  • 缓存装饰器
  • 代理装饰器
  • 上下文装饰器

参数检查(检查被装饰函数的输入输出参数类型)

  • XML-RPC:轻量级的远程过程调用协议,该协议提供了扩展,可以用来发现服务器的API,Python的xmlrpc模块实现了这个扩展。
rpc_info = {}

def xmlrpc(in_=(), out=(type(None), )):      # in_ , out 为输入输出的参数类型: in=(int, int), out=(None, type(None) , type(4)),etc
    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:]   # 去掉self
            _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):  # 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()  # 返回16进制的哈希值
    
def memoize(duration=10):
    def _memoize(function):
        def __memoize(*args, **kwargs):
            key  = compute_key(function, args, kwargs)
            # key是否在cache中
            if (key in cache and  is_obsolete(cache[key], duration)):   # 据上次函数调用时间不超过duration,直接从cache中返回结果
                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')   # globals()返回一个包含全局变量的字典,变量名的字符串为键值
            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

你可能感兴趣的:(python)