zoukankan      html  css  js  c++  java
  • Python 装饰器学习心得

        最近打算重新开始记录自己的学习过程,于是就捡起被自己废弃了一年多的博客。这篇学习笔记主要是记录近来看的有关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

  • 相关阅读:
    uva 408 Uniform Generator
    Java实现 蓝桥杯VIP 算法提高 栅格打印问题
    Java实现 蓝桥杯VIP 算法提高 栅格打印问题
    Java实现 蓝桥杯VIP 算法提高 栅格打印问题
    Java实现 蓝桥杯VIP 算法提高 打水问题
    Java实现 蓝桥杯VIP 算法提高 打水问题
    Java实现 蓝桥杯VIP 算法提高 打水问题
    Java实现 蓝桥杯VIP 算法提高 打水问题
    Java实现 蓝桥杯VIP 算法提高 不同单词个数统计
    Java实现 蓝桥杯VIP 算法提高 不同单词个数统计
  • 原文地址:https://www.cnblogs.com/boostable/p/python_decorator.html
Copyright © 2011-2022 走看看