|
|
|
|
移动端

2.2.3 装饰器(3)

《Python高级编程(第2版)》第2章语法最佳实践——类级别以下,本章将介绍现在这门语言的语法中最重要的元素,以及它们的使用技巧。本节为大家介绍装饰器。

作者:张亮/阿信 译来源:人民邮电出版社|2018-01-29 18:31

技术沙龙 | 邀您于8月25日与国美/AWS/转转三位专家共同探讨小程序电商实战

2.2.3 装饰器(3)

装饰器将函数注册到全局字典中,并将其参数和返回值保存在一个类型列表中。注意,这个示例做了很大的简化,为的是展示装饰器的参数检查功能。

使用示例如下:

  1. class RPCView:  
  2.     @xmlrpc((int, int))  # two int -> None  
  3.     def meth1(self, int1, int2):  
  4.         print('received %d and %d' % (int1, int2))  
  5.  
  6.     @xmlrpc((str,), (int,))  # string -> int  
  7.     def meth2(self, phrase):  
  8.         print('received %s' % phrase)  
  9.         return 12 

在实际读取时,这个类定义会填充rpc _ infos字典,并用于检查参数类型的特定环境中:

  1. >>> rpc_info  
  2. {'meth2': ((< class 'str'>,), (< class 'int'>,)), 'meth1': ((< class 
  3. 'int'>< class 'int'>), (,))}  
  4. >>> my = RPCView()  
  5. >>> my.meth1(1, 2)  
  6. received 1 and 2  
  7. >>> my.meth2(2)  
  8. Traceback (most recent call last):  
  9.   File "< input>", line 1, in < module> 
  10.   File "< input>", line 26, in __xmlrpc  
  11.   File "< input>", line 20, in _check_types  
  12. TypeError: arg #0 should be < class 'str'> 

(2)缓存

缓存装饰器与参数检查十分相似,不过它重点是关注那些内部状态不会影响输出的函数。每组参数都可以链接到唯一的结果。这种编程风格是函数式编程(functional programming,参见https://en.wikipedia.org/wiki/Functional_programming)的特点,当输入值有限时可以使用。

因此,缓存装饰器可以将输出与计算它所需要的参数放在一起,并在后续的调用中直接返回它。这种行为被称为memoizing(参见https://en.wikipedia.org/wiki/Memoization),很容易被实现为一个装饰器:

  1. import time  
  2. import hashlib  
  3. import pickle  
  4.  
  5. cache = {}  
  6.  
  7. def is_obsolete(entry, duration):  
  8.     return time.time() - entry['time'] > duration  
  9.  
  10. def compute_key(function, args, kw):  
  11.     key = pickle.dumps((function.__name__, args, kw))  
  12.     return hashlib.sha1(key).hexdigest()  
  13.  
  14. def memoize(duration=10):  
  15.     def _memoize(function):  
  16.         def __memoize(*args, **kw):  
  17.             key = compute_key(function, args, kw)  
  18.  
  19.             # 是否已经拥有它了?  
  20.             if (key in cache and  
  21.                 not is_obsolete(cache[key], duration)):  
  22.                 print('we got a winner')  
  23.                 return cache[key]['value']  
  24.             # 计算  
  25.             result = function(*args, **kw)  
  26.             # 保存结果  
  27.             cache[key] = {  
  28.                 'value': result,  
  29.                 'time': time.time()  
  30.             }  
  31.             return result  
  32.         return __memoize  
  33.     return _memoize 

利用已排序的参数值来构建SHA哈希键,并将结果保存在一个全局字典中。利用pickle来建立hash,这是冻结所有作为参数传入的对象状态的快捷方式,以确保所有参数都满足要求。举个例子,如果用一个线程或套接字作为参数,那么会引发PicklingError(参见https://docs.python.org/3/library/pickle.html)。duration参数的作用是,如果上一次函数调用已经过去了太长时间,那么它会使缓存值无效。

下面是一个使用示例:

  1. >>> @memoize()  
  2. ... def very_very_very_complex_stuff(a, b):  
  3. ...     # 如果在执行这个计算时计算机过热  
  4. ...     # 请考虑中止程序  
  5. ...     return a + b  
  6. ...  
  7. >>> very_very_very_complex_stuff(2, 2)  
  8. 4  
  9. >>> very_very_very_complex_stuff(2, 2)  
  10. we got a winner  
  11. 4  
  12. >>> @memoize(1) # 1秒后令缓存失效  
  13. ... def very_very_very_complex_stuff(a, b):  
  14. ...     return a + b  
  15. ...  
  16. >>> very_very_very_complex_stuff(2, 2)  
  17. 4  
  18. >>> very_very_very_complex_stuff(2, 2)  
  19. we got a winner  
  20. 4  
  21. >>> cache  
  22. {'c2727f43c6e39b3694649ee0883234cf': {'value': 4, 'time':  
  23. 1199734132.7102251)}  
  24. >>> time.sleep(2)  
  25. >>> very_very_very_complex_stuff(2, 2)  

缓存代价高昂的函数可以显著提高程序的总体性能,但必须小心使用。缓存值还可以与函数本身绑定,以管理其作用域和生命周期,代替集中化的字典。但在任何情况下,更高效的装饰器会使用基于高级缓存算法的专用缓存库。
 
第12章将会介绍与缓存相关的详细信息和技术。

(3)代理

代理装饰器使用全局机制来标记和注册函数。举个例子,一个根据当前用户来保护代码访问的安全层可以使用集中式检查器和相关的可调用对象要求的权限来实现:

  1. class User(object):  
  2.     def __init__(self, roles):  
  3.         self.roles = roles  
  4.  
  5. class Unauthorized(Exception):  
  6.     pass  
  7.  
  8. def protect(role):  
  9.     def _protect(function):  
  10.         def __protect(*args, **kw):  
  11.             user = globals().get('user')  
  12.             if user is None or role not in user.roles:  
  13.                 raise Unauthorized("I won't tell you")  
  14.             return function(*args, **kw)  
  15.         return __protect  
  16.     return _protect 

喜欢的朋友可以添加我们的微信账号:

51CTO读书频道二维码


51CTO读书频道活动讨论群:365934973

【责任编辑:book TEL:(010)68476606】

回书目   上一节   下一节
点赞 0
分享:
大家都在看
猜你喜欢

读 书 +更多

The Ruby Way(第二版)中文版

本书采用“如何解决问题”的方式阐述Ruby编程,涵盖了以下内容:Ruby术语和基本原理;数字、字符串等低级数据类型的操作;正则表达式;国际...

订阅51CTO邮刊

点击这里查看样刊

订阅51CTO邮刊