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

[经验分享] Python

[复制链接]

尚未签到

发表于 2017-5-6 09:12:39 | 显示全部楼层 |阅读模式
大家都知道装饰器是一个很著名的设计模式,经常被用于AOP(面向切面编程)的场景,较为经典的有插入日志,性能测试,事务处理,Web权限校验, Cache等。
原文链接:http://www.gbtags.com/gb/share/5563.htm
Python语言本身提供了装饰器语法(@),典型的装饰器实现如下:
 

  • @function_wrapper
  • deffunction():
  • pass
@实际上是python2.4才提出的语法糖,针对python2.4以前的版本有另一种等价的实现:
 

  • deffunction():
  • pass
  • function= function_wrapper(function)
 
装饰器的两种实现
函数包装器 - 经典实现
 

  • def function_wrapper(wrapped):
  • def _wrapper(*args,**kwargs):
  • return wrapped(*args,**kwargs)
  • return _wrapper
  • @function_wrapper
  • deffunction():
  • pass
类包装器 - 易于理解
 

  • class function_wrapper(object):
  • def __init__(self, wrapped):
  • self.wrapped = wrapped
  • def __call__(self,*args,**kwargs):
  • returnself.wrapped(*args,**kwargs)
  • @function_wrapper
  • deffunction():
  • pass
 
函数(function)自省
当我们谈到一个函数时,通常希望这个函数的属性像其文档上描述的那样,是被明确定义的,例如__name__ 和__doc__ 。
针对某个函数应用装饰器时,这个函数的属性就会发生变化,但这并不是我们所期望的。
 

  • def function_wrapper(wrapped):
  • def _wrapper(*args,**kwargs):
  • return wrapped(*args,**kwargs)
  • return _wrapper
  • @function_wrapper
  • deffunction():
  • pass
  • >>>print(function.__name__)
  • _wrapper
python标准库提供了functools.wraps() ,来解决这个问题。
 

  • import functools
  • def function_wrapper(wrapped):
  • @functools.wraps(wrapped)
  • def _wrapper(*args,**kwargs):
  • return wrapped(*args,**kwargs)
  • return _wrapper
  • @function_wrapper
  • deffunction():
  • pass
  • >>>print(function.__name__)
  • function
然而,当我们想要获取被包装函数的参数(argument )或源代码(source code)时,同样不能得到我们想要的结果。
 

  • import inspect
  • def function_wrapper(wrapped):...
  • @function_wrapper
  • deffunction(arg1, arg2):pass
  • >>>print(inspect.getargspec(function))
  • ArgSpec(args=[], varargs='args', keywords='kwargs', defaults=None)
  • >>>print(inspect.getsource(function))
  • @functools.wraps(wrapped)
  • def _wrapper(*args,**kwargs):
  • return wrapped(*args,**kwargs)
 
包装类方法(@classmethod)
当包装器(@function_wrapper )被应用于@classmethod时,将会抛出如下异常:
 

  • classClass(object):
  • @function_wrapper
  • @classmethod
  • def cmethod(cls):
  • pass
  • Traceback(most recent call last):
  • File"<stdin>", line 1,in<module>
  • File"<stdin>", line 3,inClass
  • File"<stdin>", line 2,in wrapper
  • File".../functools.py", line 33,in update_wrapper
  • setattr(wrapper, attr, getattr(wrapped, attr))
  • AttributeError:'classmethod'object has no attribute '__module__'
因为@classmethod 在实现时,缺少functools.update_wrapper 需要的某些属性。这是functools.update_wrapper 在python2中的bug,3.2版本已被修复,参考http://bugs.python.org/issue3445。
然而,在python3下执行,另一个问题出现了:
 

  • classClass(object):
  • @function_wrapper
  • @classmethod
  • def cmethod(cls):
  • pass
  • >>>Class.cmethod()
  • Traceback(most recent call last):
  • File"classmethod.py", line 15,in<module>
  • Class.cmethod()
  • File"classmethod.py", line 6,in _wrapper
  • return wrapped(*args,**kwargs)
  • TypeError:'classmethod'objectisnot callable
这是因为包装器认定被包装的函数(@classmethod )是可以直接被调用的,但事实并不一定是这样的。被包装的函数实际上可能是描述符(descriptor ),意味着为了使其可调用,该函数(描述符)必须被正确地绑定到某个实例上。关于描述符的定义,可以参考https://docs.python.org/2/howto/descriptor.html。
 
总结 - 简单并不意味着正确
尽管大家实现装饰器所用的方法通常都很简单,但这并不意味着它们一定是正确的并且始终能正常工作。
如同上面我们所看到的,functools.wraps() 可以帮我们解决__name__ 和__doc__ 的问题,但对于获取函数的参数(argument)或源代码( source code )则束手无策。
以上问题,wrapt都可以帮忙解决,详细用法可参考其官方文档:http://wrapt.readthedocs.org
本文系iyunv工程师编译整理。iyunv是中国基础软件领域的新兴领军企业,能帮助企业用户和开发者轻松实现:缓慢的程序代码和SQL语句的实时抓取。想阅读更多技术文章,请访问iyunv官方技术博客。
 
原文链接:http://www.gbtags.com/gb/share/5563.htm

运维网声明 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-373616-1-1.html 上篇帖子: Python Django1.5入门到精通视频教程 下篇帖子: 零基础学python-8.7 字典常用方法
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

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

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

扫描微信二维码查看详情

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


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


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


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



合作伙伴: 青云cloud

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