设为首页 收藏本站
查看: 921|回复: 0

[经验分享] 学习python decorator模块

[复制链接]

尚未签到

发表于 2017-4-27 06:06:51 | 显示全部楼层 |阅读模式
本文简介
decorator模块是 Michele Simionato 为简化python的decorator的使用难度而开发的,使用它,您可以更加容易的使用decorator机制写出可读性、可维护性更好的代码。
本文大部分翻译自下面这篇文档: www.phyast.pitt.edu/~micheles/python/documentation.html , 之中或会加入自己的理解或注释
decorator 模块


作者:Michele SimionatoE-mail:michele.simionato@gmail.com版本:2.0.1下载:http://www.phyast.pitt.edu/~micheles/python/decorator-2.0.1.zip网络安装:easy_install decoratorLicense:Python license
简介



Python 2.4 的 decorators 是一个展现语法糖的有趣的例子: 原理上, 它们的引入没有改变任何东西,因为它们没有提供任何新的在原语言里面不存在的功能; 实践中, 它们的引入能够显著的改善我们的Python代码结构. 我相信这种改变是出于好意的, 基于以下理由,decorators是一些非常好的想法:

  • decorators 帮助减少教条化/样板化的代码;
  • decorators 有助于分离关注点
  • decorators 有助于增强可读性和可维护性
  • decorators 是非常直接/清晰的

但是,到现在为止(译者注: 到Python 2.5 里面已经有所改善),正确的定制一个decorators需要一些经验, 它比我们想像的要难于使用。例如,典型的decorators的实现涉及到嵌套(nested)函数,而我们都知道:flat 比 nested 好。
decorator的目的是为普通开发人员简化dccorators的使用,并且通过一些有用的例子来推广decorators的使用,例如:memoize,tracing,redirecting_stdout, locked等等。
模块的核心是一个叫做decorator的decorator工厂函数。所有在这里讨论的简单的decorators解决方案都是基于decorator模块的。通过执行如下命令后产生的 _main.py 文件中包含了这些所有的这些代码:
> python doctester.py documentation.txt
同时,该命令还会运行所有的作为测试用例存在的例子。
定义

  从技术上来讲, 任何可以被调用的、带一个参数的Python对象都可以被当作是一个decorator。 然而,这个定义因为过于宽泛而显得并无真正得用处。为方便起见,我们可以把decorator分为两类:

  • 保留签名的(signature-preserving)decorators,例如:将一个函数A作为可调用对象(decorators)的输入并返回一个函数B,函数A和B的签名是相同的;
  • 改变签名的(signature-changing)decorators, 例如:将一个函数A作为可调用对象(decorators)的输入并返回一个函数B,函数A和B的签名是不相同的,或者该decorator的返回值就不是一个可调用对象。

Signature-changing decorators有它们的用处,例如:内部类 staticmethod 和 classmethod 就属于这一类,因为它们接受函数作为输入并返回一个descriptor对象,这个对象不是函数,也不是可调用对象。
但是,signature-preserving decorators 则更加通用,更加容易理解,尤其是这一类decorators能够被组合起来使用,而其他decorators通常是不能够组合使用的(如staticmethod和classmethod)。
从零开始写一个signature-preserving 的decorator 不是那么浅显的,尤其是当你想实现一个正确的能够接受任意签名的decorator时,一个简单的例子将阐明这个问题。


问题的描述

假设你想跟踪一个函数的执行:这是一个常见的使用decorator的例子,很多地方你都能看到类似的代码



python 代码


  • try:  
  •     from functools import update_wrapper  
  • except ImportError: # using Python version < 2.5  
  •     def decorator_trace(f):  
  •         def newf(*args, **kw):  
  •            print "calling %s with args %s, %s" % (f.__name__, args, kw)  
  •            return f(*args, **kw)  
  •         newf.__name__ = f.__name__  
  •         newf.__dict__.update(f.__dict__)  
  •         newf.__doc__ = f.__doc__  
  •         newf.__module__ = f.__module__  
  •         return newf  
  • else: # using Python 2.5+  
  •     def decorator_trace(f):  
  •         def newf(*args, **kw):  
  •             print "calling %s with args %s, %s" % (f.__name__, args, kw)  
  •             return f(*args, **kw)  
  •         return update_wrapper(newf, f)  




(译者注:上面的代码中虽然有修改结果对象的自省信息(或元信息),但是修改得不全面,如其签名信息就没有修改为与被修饰对象保持一致)
上面代码是想实现一个能够接受一般签名的decorator,不幸的是,该实现并没有定义为一个signature-preserving的decorator,因为大体上说 decorator_trace 返回了一个与被修饰函数不同的签名。
思考下面的例子:

>>> @decorator_trace
... def f1(x):
...     pass
这里原先的函数只接受一个参数,而修饰后的函数却接受任意的参数和关键字参数。
>>> from inspect import getargspec
>>> print getargspec(f1)
([], 'args', 'kw', None)

这就意味这自省工具如:pydoc将会给出关于函数f1的错误的签名信息,这是一个美丽的错误:pydoc将告诉你该函数能够接受一个通用的签名:*args, **kw,但是当你使用超过一个的参数去调用该函数时,你将会得到如下错误:
>>> f1(0, 1)
Traceback (most recent call last):
   ...
TypeError: f1() takes exactly 1 argument (2 given)

解决方案

这个方案提供了一个通用的 decorators  工厂,它对应用程序员隐藏了实现 signature-preserving  的 decorator 的复杂性。该工厂允许在不使用嵌套函数或嵌套类的情况下定义decorators, 下面是一个告诉你怎样定义 decorator_trace 的简单例子。
首先,导入decorator模块:
>>> from decorator import decorator
然后定义一个辅助函数f,该函数具有如下signature:(f, *args, **kw),该函数只是简单的做如下调用f(args, kw):
python 代码

 


  • def trace(f, *args, **kw):  
  •     print "calling %s with args %s, %s" % (f.func_name, args, kw)  
  •     return f(*args, **kw)  



decorator 模块能够把帮助函数转变成一个 signature-preserving 对象,例如:一个接受一个函数作为输入的可调用对象,并返回一个被修饰过的函数,该函数具有与原函数一致的签名,这样,你就能够这样写:
>>> @decorator(trace)
... def f1(x):
...     pass
很容易就验证函数 f1 正常工作了
>>> f1(0)
calling f1 with args (0,), {}
并且它具有正确的签名:
>>> print getargspec(f1)
(['x'], None, None, None)
同样的,该decorator对象对具有其他签名的函数也是有效的:
>>> @decorator(trace)
... def f(x, y=1, z=2, *args, **kw):
...     pass

>>> f(0, 3)
calling f with args (0, 3, 2), {}

>>> print getargspec(f)
甚至包括下面这些具有奇特签名的函数也能够正常工作:
>>> @decorator(trace)
... def exotic_signature((x, y)=(1,2)): return x+y
>>> print getargspec(exotic_signature)
([['x', 'y']], None, None, ((1, 2),))
>>> exotic_signature()
calling exotic_signature with args ((1, 2),), {}
3
(['x', 'y', 'z'], 'args', 'kw', (1, 2))

decorator is a Decorator

工厂函数decorator本身就可以被当作是一个 signature-changing 的decorator, 就和 classmethod 和 staticmethod 一样,不同的是这两个内部类返回的是一般的不可调用的对象,而decorator返回的是 signature-preserving 的decorators, 例如带单参数的函数。这样,你能够这样写:
>>> @decorator
... def tracing(f, *args, **kw):
...     print "calling %s with args %s, %s" % (f.func_name, args, kw)
...     return f(*args, **kw)
 这中惯用法实际上是把 tracing 重定义为一个 decorator , 我们能够很容易的检测到tracing的签名被更改了:
>>> print getargspec(tracing)
(['f'], None, None, None)
这样,tracing能够被当作一个decorator使用,下面的代码能够工作:
>>> @tracing
... def func(): pass

>>> func()
calling func with args (), {}

BTW,你还能够对 lambda 函数应用该 decorator:
>>> tracing(lambda : None)()
calling <lambda> with args (), {}</lambda>
下面开始讨论decorators的用法。
缓存化(memoize)

这里讨论的decorator实现了memoize模式,它可以把函数调用结果存储在一个字典对象中,下次使用相同参数调用该函数时,就可以直接从该字典对象里面获取结果而无需重新计算。
memoize 代码

 


  • from decorator import *  
  •   
  • def getattr_(obj, name, default_thunk):  
  •     "Similar to .setdefault in dictionaries."  
  •     try:  
  •         return getattr(obj, name)  
  •     except AttributeError:  
  •         default = default_thunk()  
  •         setattr(obj, name, default)  
  •         return default  
  •   
  • @decorator  
  • def memoize(func, *args):  
  •     dic = getattr_(func, "memoize_dic", dict)  
  •     # memoize_dic is created at the first call  
  •     if args in dic:  
  •         return dic[args]  
  •     else:  
  •         result = func(*args)  
  •         dic[args] = result  
  •         return result  



下面时使用测试:
>>> @memoize
... def heavy_computation():
...     time.sleep(2)
...     return "done"

>>> print heavy_computation() # the first time it will take 2 seconds
done

>>> print heavy_computation() # the second time it will be instantaneous
done

作为练习, 您可以尝试不借助于decorator工厂来正确的实现memoize。
注意:这个memoize实现只有当函数没有关键字参数时才能够正常工作,因为实际上不可能正确的缓存具有可变参数的函数。您可以放弃这个需求,允许有关键字参数存在,但是,当有关键字参数被传入时,其结果是不能够被缓存的。具体例子请参照http://www.python.org/moin/PythonDecoratorLibrary
锁(locked)

不想再翻这一小节了!这一小节本来已经翻译了,但是由于系统故障导致被丢失了,而且因为Python2.5中已经实现了with语句,因此这一小节的内容也就显得不是那么重要了。
代码附上:
locked的实现

 


  • import threading  
  •   
  • @decorator  
  • def locked(func, *args, **kw):  
  •     lock = getattr_(func, "lock", threading.Lock)  
  •     lock.acquire()  
  •     try:  
  •         result = func(*args, **kw)  
  •     finally:  
  •         lock.release()  
  •     return result  



锁的使用

 


  • import time  
  •   
  • datalist = [] # for simplicity the written data are stored into a list.  
  •   
  • @locked  
  • def write(data):  
  •     "Writing to a sigle-access resource"  
  •     time.sleep(1)  
  •     datalist.append(data)  



延时化和线程化(delayed and threaded)

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-369637-1-1.html 上篇帖子: python环境搭建【virtualenv实现环境隔离】 下篇帖子: python 易混地带
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表