Python 装饰器学习心得
2015-12-13 16:12
591 查看
最近打算重新开始记录自己的学习过程,于是就捡起被自己废弃了一年多的博客。这篇学习笔记主要是记录近来看的有关Python装饰器的东西。
0. 什么是装饰器?
本质上来说,装饰器其实就是一个特殊功能的函数,这个特殊的功能就是:装饰另一个函数。举一个最简单的例子来说:
这里identify其实是一个装饰器,这个装饰器对输入的参数f不进行任何修饰,然后返回这个参数。其中的打印语句是用来判断这个装饰器是什么时候被调用的。接下去,我们可以这样用这个装饰器:
装饰器的语法是以@开头,然后跟着装饰器函数的名字,然后一些可选的装饰器函数的参数。紧跟着是被修饰的函数的定义。上述带装饰器的函数定义可以看成以下语法糖:
写一个简单的类来对上述例子做一下测试:
为了简单起见,我这里没有把identify装饰器写成类静态方法,因为类静态方法的定义也是利用装饰器来的。运行上述的测试代码,得到如下的输出:
可以看到装饰器函数比类实例的初始化还早,这说明在利用装饰器定义foo函数的时候装饰器函数已经被调用了。
1. 注册函数的装饰器。
接下去,我们来定义一个稍微复杂一点的装饰器,这个装饰器能把被装饰的函数注册到一个全局的字典里面。
首先,这是一个带参数的装饰器。我们可以先看看怎么用这个装饰器,
接下去,结合装饰器的定义来分析一下上述的代码。首先对于这句定义 @register1('RegisterDecorator')可以理解成@(register1('RegisterDecorator'))这样的优先级,也就是我们先传入参数'RegisterDecorator'来调用register1函数,可以看到register1函数其实是返回一个_register函数,在这里我更愿意把_register看成真正的装饰器。所以这句定义@register1('RegisterDecorator')就可以看成@_register,而这个_register其实是定义在带有参数'RegisterDecorator'信息的作用域内部。接下去的@_register就跟不带参数的装饰器一样了,所以在定义foo1函数的时候_register函数会被调用,在这个函数内部,foo1会被注册在一个全局的字典内部,然后返回一个跟foo1一样功能的函数。
2.functools装饰器工具
我们在来深入讨论一下上述装饰器,上述装饰器虽然达到我们注册函数的装饰作用,但是其实你会发现,这个被装饰的函数foo1已经不是原来的foo1,比如foo1.__doc__和foo1.__name__已经不是原来foo1时的"This is the foo1 doc"和"foo1"。这是怎么回事呢?因为装饰器其实只是一个语法糖,被装饰的foo1其实等于_register里面返回的wrapper函数,也就是foo1 = _register(foo1),而_register返回的正是wrapper函数,所以此时的foo1.__doc__和foo1.__name__应该是"This is wrapper doc."和"wrapper"。那么要如何避免上述的情况的呢,那就是利用装饰器工具functools。其实functools也是提供一些装饰器,这个装饰器可以保证你在定义装饰器时保留原来函数的__doc__和__name__属性。具体例子如下:
register2和register1唯一不同的是,regisrer2返回的是被装饰器 @functools.wraps 装饰过的warpper函数。上述测试代码的输出为:
关于上述的测试代码托管在github上:https://github.com/fengzaihou/PythonLearning/tree/master/Decorators
0. 什么是装饰器?
本质上来说,装饰器其实就是一个特殊功能的函数,这个特殊的功能就是:装饰另一个函数。举一个最简单的例子来说:
def identify(f): print 'Decorator identify called.' return f
这里identify其实是一个装饰器,这个装饰器对输入的参数f不进行任何修饰,然后返回这个参数。其中的打印语句是用来判断这个装饰器是什么时候被调用的。接下去,我们可以这样用这个装饰器:
@identify def foo(): print 'foo called.'
装饰器的语法是以@开头,然后跟着装饰器函数的名字,然后一些可选的装饰器函数的参数。紧跟着是被修饰的函数的定义。上述带装饰器的函数定义可以看成以下语法糖:
def foo(): print 'foo called.' foo = identify(foo)
写一个简单的类来对上述例子做一下测试:
# Decorators def identify(f): print 'Decorator identify called.' return f class SimpleDecorator(object): """ This is my first decorator test class. """ def __init__(self): super(SimpleDecorator, self).__init__() print 'SimpleDecorator init.' @identify def foo(self): print 'Foo called.' def test(self): self.foo() if __name__ == '__main__': print '---- Test SimpleDecorator begin. ----' simpleDecorator = SimpleDecorator() simpleDecorator.test() print '---- Test SimpleDecorator end. ----'
为了简单起见,我这里没有把identify装饰器写成类静态方法,因为类静态方法的定义也是利用装饰器来的。运行上述的测试代码,得到如下的输出:
可以看到装饰器函数比类实例的初始化还早,这说明在利用装饰器定义foo函数的时候装饰器函数已经被调用了。
1. 注册函数的装饰器。
接下去,我们来定义一个稍微复杂一点的装饰器,这个装饰器能把被装饰的函数注册到一个全局的字典里面。
_functions = {} def register1(class_name): def _register(f): """ This is register1 doc. """ print 'Decorator register1 called.' global _functions name = class_name + '.' + f.__name__ _functions[name] = f print 'Function %s is registed.' % name def warpper(*args, **kwargs): """ This is warpper doc. """ f(*args, **kwargs) return warpper return _register
首先,这是一个带参数的装饰器。我们可以先看看怎么用这个装饰器,
@register1('RegisterDecorator') def foo1(): """ This is the foo1 doc. """ print 'Foo1 called.'
接下去,结合装饰器的定义来分析一下上述的代码。首先对于这句定义 @register1('RegisterDecorator')可以理解成@(register1('RegisterDecorator'))这样的优先级,也就是我们先传入参数'RegisterDecorator'来调用register1函数,可以看到register1函数其实是返回一个_register函数,在这里我更愿意把_register看成真正的装饰器。所以这句定义@register1('RegisterDecorator')就可以看成@_register,而这个_register其实是定义在带有参数'RegisterDecorator'信息的作用域内部。接下去的@_register就跟不带参数的装饰器一样了,所以在定义foo1函数的时候_register函数会被调用,在这个函数内部,foo1会被注册在一个全局的字典内部,然后返回一个跟foo1一样功能的函数。
2.functools装饰器工具
我们在来深入讨论一下上述装饰器,上述装饰器虽然达到我们注册函数的装饰作用,但是其实你会发现,这个被装饰的函数foo1已经不是原来的foo1,比如foo1.__doc__和foo1.__name__已经不是原来foo1时的"This is the foo1 doc"和"foo1"。这是怎么回事呢?因为装饰器其实只是一个语法糖,被装饰的foo1其实等于_register里面返回的wrapper函数,也就是foo1 = _register(foo1),而_register返回的正是wrapper函数,所以此时的foo1.__doc__和foo1.__name__应该是"This is wrapper doc."和"wrapper"。那么要如何避免上述的情况的呢,那就是利用装饰器工具functools。其实functools也是提供一些装饰器,这个装饰器可以保证你在定义装饰器时保留原来函数的__doc__和__name__属性。具体例子如下:
import functools _functions = {} def register1(class_name): def _register(f): """ This is register1 doc. """ print 'Decorator register1 called.' global _functions name = class_name + '.' + f.__name__ _functions[name] = f print 'Function %s is registed.' % name def warpper(*args, **kwargs): """ This is warpper doc. """ f(*args, **kwargs) return warpper return _register def register2(class_name): def _register(f): """ This is register2 doc. """ print 'Decorator register2 called.' global _functions name = class_name + '.' + f.__name__ _functions[name] = f print 'Function %s is registed.' % name @functools.wraps(f) def warpper(*args, **kwargs): """ This is warpper doc. """ f(*args, **kwargs) return warpper return _register class RegisterDecorator(object): """ This is register Decorator. The decorator registed the function in golbal dict. """ def __init__(self): super(RegisterDecorator, self).__init__() print 'RegisterDecorator init.' @register1('RegisterDecorator') def foo1(self): """ This is the foo1 doc. """ print 'Foo1 called.' @register2('RegisterDecorator') def foo2(self): """ This is the foo2 doc. """ print 'Foo2 called' def test(self): self.foo1() self.foo2() pass if __name__ == '__main__': print '---- Test RegisterDecorator begin. ----' registerDecorator = RegisterDecorator() registerDecorator.test() print 'The doc of foo1 is: ', registerDecorator.foo1.__doc__ print 'The name of foo1 is: ', registerDecorator.foo1.__name__ print 'The doc of foo2 is: ', registerDecorator.foo2.__doc__ print 'The name of foo2 is: ', registerDecorator.foo2.__name__ print '---- Test RegisterDecorator end. ----'
register2和register1唯一不同的是,regisrer2返回的是被装饰器 @functools.wraps 装饰过的warpper函数。上述测试代码的输出为:
关于上述的测试代码托管在github上:https://github.com/fengzaihou/PythonLearning/tree/master/Decorators
相关文章推荐
- python3.5读取网页代码,并保存
- 04 Python正则表达式 爬虫程序 变量的引用,浅拷贝,深拷贝 多线程 进程锁 数据库模块
- Python3切换华为hi link设备数据开关
- 分享Python字符串关键点
- python 多线程学习
- 03 Python 文件系统 访问权限 函数 类与面向对象 自定义模块
- Python实时获取cmd的输出
- [从头学python] 第03节 让我们来学习math
- python中的算术操作符
- 【Python】Mysql中文乱码问题与MySQLdb对Mysql操作类的改进
- python 安装selenium环境
- python异常记录
- 02 Python元组 字典 数据类型 if while for 迭代
- kmeans聚类的简介和代码(python)
- 01 Python 安装 切片 查找替换 print 函数 return 局域代码块 注释 帮助 强制转换
- python模块安装
- python pip&virtualenv(ubuntu 15.10)
- 通过wireshark,以及python代码收发邮件,了解smtp协议,pop协议工作过程
- 零基础入门学习Python(9):序列
- python 获取一周的天气内容