码迷,mamicode.com
首页 > 编程语言 > 详细

Python 装饰器学习心得

时间:2015-12-13 17:21:53      阅读:187      评论:0      收藏:0      [点我收藏+]

标签:

    最近打算重新开始记录自己的学习过程,于是就捡起被自己废弃了一年多的博客。这篇学习笔记主要是记录近来看的有关Python装饰器的东西。

0. 什么是装饰器?

    本质上来说,装饰器其实就是一个特殊功能的函数,这个特殊的功能就是:装饰另一个函数。举一个最简单的例子来说:

1 def identify(f):
2     print Decorator identify called.
3     return f

这里identify其实是一个装饰器,这个装饰器对输入的参数f不进行任何修饰,然后返回这个参数。其中的打印语句是用来判断这个装饰器是什么时候被调用的。接下去,我们可以这样用这个装饰器:

1 @identify
2 def foo():
3     print foo called.

装饰器的语法是以@开头,然后跟着装饰器函数的名字,然后一些可选的装饰器函数的参数。紧跟着是被修饰的函数的定义。上述带装饰器的函数定义可以看成以下语法糖:

1 def foo():
2     print foo called.
3 foo = identify(foo)

写一个简单的类来对上述例子做一下测试:

 1 # Decorators
 2 def identify(f):
 3     print Decorator identify called.
 4     return f
 5 class SimpleDecorator(object):
 6     """
 7     This is my first decorator test class.
 8     """
 9 
10     def __init__(self):
11         super(SimpleDecorator, self).__init__()
12         print SimpleDecorator init.
13 
14     @identify
15     def foo(self):
16         print Foo called.
17 
18     def test(self):
19         self.foo()
20 if __name__ == __main__:
21     print ---- Test SimpleDecorator begin. ----
22     simpleDecorator = SimpleDecorator()
23     simpleDecorator.test()
24     print ---- Test SimpleDecorator end. ----

为了简单起见,我这里没有把identify装饰器写成类静态方法,因为类静态方法的定义也是利用装饰器来的。运行上述的测试代码,得到如下的输出:

技术分享

可以看到装饰器函数比类实例的初始化还早,这说明在利用装饰器定义foo函数的时候装饰器函数已经被调用了。

1. 注册函数的装饰器。

接下去,我们来定义一个稍微复杂一点的装饰器,这个装饰器能把被装饰的函数注册到一个全局的字典里面。

 1 _functions = {}
 2 def register1(class_name):
 3     def _register(f):
 4         """
 5         This is register1 doc.
 6         """
 7         print Decorator register1 called.
 8         global _functions
 9         name = class_name + . + f.__name__
10         _functions[name] = f
11         print Function %s is registed. % name
12         def warpper(*args, **kwargs):
13             """
14             This is warpper doc.
15             """
16             f(*args, **kwargs)
17         return warpper
18     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__属性。具体例子如下:

 1 import functools
3 4 5 _functions = {} 6 def register1(class_name): 7 def _register(f): 8 """ 9 This is register1 doc. 10 """ 11 print Decorator register1 called. 12 global _functions 13 name = class_name + . + f.__name__ 14 _functions[name] = f 15 print Function %s is registed. % name 16 def warpper(*args, **kwargs): 17 """ 18 This is warpper doc. 19 """ 20 f(*args, **kwargs) 21 return warpper 22 return _register 23 24 25 def register2(class_name): 26 def _register(f): 27 """ 28 This is register2 doc. 29 """ 30 print Decorator register2 called. 31 global _functions 32 name = class_name + . + f.__name__ 33 _functions[name] = f 34 print Function %s is registed. % name 35 @functools.wraps(f) 36 def warpper(*args, **kwargs): 37 """ 38 This is warpper doc. 39 """ 40 f(*args, **kwargs) 41 return warpper 42 return _register 43 44 class RegisterDecorator(object): 45 """ 46 This is register Decorator. 47 The decorator registed the function in golbal dict. 48 """ 49 50 def __init__(self): 51 super(RegisterDecorator, self).__init__() 52 print RegisterDecorator init. 53 54 @register1(RegisterDecorator) 55 def foo1(self): 56 """ 57 This is the foo1 doc. 58 """ 59 print Foo1 called. 60 61 @register2(RegisterDecorator) 62 def foo2(self): 63 """ 64 This is the foo2 doc. 65 """ 66 print Foo2 called 67 68 def test(self): 69 self.foo1() 70 self.foo2() 71 pass 72 73 if __name__ == __main__: 74 print ---- Test RegisterDecorator begin. ---- 75 registerDecorator = RegisterDecorator() 76 registerDecorator.test() 77 print The doc of foo1 is: , registerDecorator.foo1.__doc__ 78 print The name of foo1 is: , registerDecorator.foo1.__name__ 79 print The doc of foo2 is: , registerDecorator.foo2.__doc__ 80 print The name of foo2 is: , registerDecorator.foo2.__name__ 81 print ---- Test RegisterDecorator end. ----

register2和register1唯一不同的是,regisrer2返回的是被装饰器 @functools.wraps 装饰过的warpper函数。上述测试代码的输出为:

技术分享

关于上述的测试代码托管在github上:https://github.com/fengzaihou/PythonLearning/tree/master/Decorators

 

Python 装饰器学习心得

标签:

原文地址:http://www.cnblogs.com/boostable/p/python_decorator.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!