Welcome 微信登录
编程资源 图片资源库 蚂蚁家优选 PDF转换器

首页 / 操作系统 / Linux / Python - 装饰器使用过程中的误区

装饰器基本概念

大家都知道装饰器是一个很著名的设计模式,经常被用于AOP(面向切面编程)的场景,较为经典的有插入日志,性能测试,事务处理,Web权限校验Cache等。Python语言本身提供了装饰器语法(@),典型的装饰器实现如下:@function_wrapperdef function(): pass@实际上是python2.4才提出的语法糖,针对python2.4以前的版本有另一种等价的实现:def function():passfunction = function_wrapper(function)

装饰器的两种实现

函数包装器 - 经典实现def function_wrapper(wrapped):def _wrapper(*args, **kwargs):return wrapped(*args, **kwargs)return _wrapper @function_wrapperdef function():pass类包装器 - 易于理解class function_wrapper(object):def __init__(self, wrapped):self.wrapped = wrappeddef __call__(self, *args, **kwargs):return self.wrapped(*args, **kwargs)@function_wrapperdef function():pass

函数(function)自省

当我们谈到一个函数时,通常希望这个函数的属性像其文档上描述的那样,是被明确定义的,例如__name____doc__ 。针对某个函数应用装饰器时,这个函数的属性就会发生变化,但这并不是我们所期望的。def function_wrapper(wrapped):def _wrapper(*args, **kwargs):return wrapped(*args, **kwargs)return _wrapper @function_wrapperdef function():pass >>> print(function.__name__)_wrapperpython标准库提供了functools.wraps() ,来解决这个问题。import functools def function_wrapper(wrapped):@functools.wraps(wrapped)def _wrapper(*args, **kwargs):return wrapped(*args, **kwargs)return _wrapper @function_wrapperdef function():pass >>> print(function.__name__)function然而,当我们想要获取被包装函数的参数(argument )或源代码(source code)时,同样不能得到我们想要的结果。import inspect def function_wrapper(wrapped): ...@function_wrapperdef function(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时,将会抛出如下异常:class Class(object):@function_wrapper@classmethoddef cmethod(cls):pass Traceback (most recent call last):File "<stdin>", line 1, in <module>File "<stdin>", line 3, in ClassFile "<stdin>", line 2, in wrapperFile ".../functools.py", line 33, in update_wrappersetattr(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下执行,另一个问题出现了:class Class(object):@function_wrapper@classmethoddef 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 _wrapperreturn wrapped(*args, **kwargs)TypeError: "classmethod" object is not callable这是因为包装器认定被包装的函数(@classmethod )是可以直接被调用的,但事实并不一定是这样的。被包装的函数实际上可能是描述符(descriptor ),意味着为了使其可调用,该函数(描述符)必须被正确地绑定到某个实例上。关于描述符的定义,可以参考https://docs.python.org/2/howto/descriptor.html。

总结 - 简单并不意味着正确

尽管大家实现装饰器所用的方法通常都很简单,但这并不意味着它们一定是正确的并且始终能正常工作。如同上面我们所看到的,functools.wraps() 可以帮我们解决__name____doc__ 的问题,但对于获取函数的参数(argument)或源代码( source code )则束手无策。以上问题,wrapt都可以帮忙解决,详细用法可参考其官方文档:http://wrapt.readthedocs.org无需操作系统直接运行 Python 代码  http://www.linuxidc.com/Linux/2015-05/117357.htmCentOS上源码安装Python3.4  http://www.linuxidc.com/Linux/2015-01/111870.htm《Python核心编程 第二版》.(Wesley J. Chun ).[高清PDF中文版] http://www.linuxidc.com/Linux/2013-06/85425.htm《Python开发技术详解》.( 周伟,宗杰).[高清PDF扫描版+随书视频+代码] http://www.linuxidc.com/Linux/2013-11/92693.htmPython脚本获取Linux系统信息 http://www.linuxidc.com/Linux/2013-08/88531.htm在Ubuntu下用Python搭建桌面算法交易研究环境 http://www.linuxidc.com/Linux/2013-11/92534.htmPython 语言的发展简史 http://www.linuxidc.com/Linux/2014-09/107206.htmPython 的详细介绍:请点这里
Python 的下载地址:请点这里本文永久更新链接地址:http://www.linuxidc.com/Linux/2015-06/118992.htm