zoukankan      html  css  js  c++  java
  • 装饰器Decorator(函数的装饰)

    一。LEGB函数作用域的优先级和解析

        函数是function的一个对象,被调用完后内部变量就会被回收,被引用的除外(例如return的变量)

        1.   local :函数内部作用域

        2.  enclosing :函数内部和内嵌函数之间(一般是闭包使用)

        3.  global :全局作用域

        4.  build-in :内置作用域(Python自带的)

        当多个作用域有相同的变量时,优先级高的会覆盖优先级低的,优先级从高到低1到4.

    二。闭包

       1.  什么是闭包

    >>> def my_func(val):
    ...     def func():
    ...         print val
    ...     return func
    ... 
    >>> f = my_func('a')
    >>> f()
    a
    >>> 

        在my_func这个函数里,内嵌的func函数就是闭包,而闭包调用了enclosing作用域的val,my_func函数调用完后,val理应被回收了,但却还能被闭包调用,这是因为闭包的__closure__属性。

       2. 闭包的__closure__属性

    >>> def my_func(val):
    ...     print '%x' % id(val)
    ...     def func():
    ...         print val
    ...     return func
    ... 
    >>> f = my_func('a')
    10ce56580
    >>> print f.__closure__
    (<cell at 0x10cf52980: str object at 0x10ce56580>,)
    >>> 

        可以看到闭包的__closure__属性保存了一个内存地址,而这个内存地址就是my_func函数里变量val的内存地址。这就是val没被回收的原因,这是闭包的一个强大的作用之一。

    三。装饰器(Decorator): 在代码运行期间动态增加功能的方式。

       1.  用闭包装饰函数my_func_01,为他添加打印的b的功能

    >>> def my_func(fun):
    ...     def func():
    ...         print 'b'
    ...         return fun()
    ...     return func
    ... 
    >>> def my_func_01():
    ...     print 'a'
    ... 
    >>> my_func_01 = my_func(my_func_01)
    >>> my_func_01()
    b
    a
    >>> 
    

       2.  用语法糖@简化闭包的装饰

    >>> def my_func(fun):
    ...     def func():
    ...         print 'b'
    ...         return fun()
    ...     return func
    ... 
    >>> @my_func
    ... def my_func_01():
    ...     print 'a'
    ... 
    >>> my_func_01()
    b
    a
    >>> 

      所以可知道@my_func就等于 my_func_01 = my_func(my_func_01)

    四。简单函数装饰

        为my_fun(a) 函数添加一个打印功能   print 'add_fun'

       @add_log  就相当于   my_fun = add_log(my_fun)

    >>> def add_log(func):
    ...     def wrapper(*args,**kw):
    ...             print 'add_fun'
    ...             return func(*args,**kw)
    ...     return wrapper
    ... 
    >>> @add_log
    ... def my_fun(a):
    ...     print 'my_fun'
    ...     return 'a'
    ... 
    >>> my_fun(4)
    add_fun
    my_fun
    'a'
    >>> 

         my_fun 指向了新的函数 wrapper(*args,**kw) , return func(*args,**kw) 而不是 return func ,这样一来,my_fun 所传入的参数就要和 my_fun(a) 的参数统一起来(当然也可以这样 my_fun(*args,**kw)) , 然后返回 func(*args,**kw) 的执行结果给 my_fun 变量,就实现了装饰作用。

    五。带参数的装饰器

        可以传入参数的装饰器,也可以这样 def add_log(*args,**kw): 来传多个参数。

        @add_log('qwe')    就相当于 my_fun = add_log('wer')(my_fun) , 也就相当于 my_fun1 = add_log('wer') 和 @my_fun1 结合. 

    >>> def add_log(text):
    ...     def log_decorator(func):
    ...             def wrapper(*args,**kw):
    ...                 print 'add_fun'
    ...                 print text
    ...                 return func(*args,**kw)
    ...         return wrapper
    ...     return log_decorator
    ... 
    >>> @add_log('qwe')
    ... def my_fun(a):
    ...     print 'my_fun'
    ...     return 'a'
    ... 
    >>> my_fun(4)
    add_fun
    qwe
    my_fun
    'a'
    >>> 

    六。完善装饰器

         my_fun 指向了新的函数 ,那一些属性就不是原来 my_fun 函数的了,这对有些依赖函数签名的代码执行就会出错(其实并不懂啥是依赖函数签名的代码0.0)。

    >>> my_fun.__name__
    'wrapper'
    >>> 

       就要在装饰器里添加 wrapper.__name__ = my_fun.__name__ 和 wrapper.__doc__ = my_fun.__doc__ 等代码,可以直接用Python内置的functools.wraps来处理。

    >>> import functools
    >>> def add_log(func):
    ...     @functools.wraps(func)
    ...     def wrapper(*args,**kw):
    ...             print 'add_fun'
    ...             return func(*args,**kw)
    ...     return wrapper
    ... 
    >>> @add_log
    ... def my_fun(a):
    ...     print 'my_fun'
    ...     return 'a'
    ... 
    >>> print my_fun.__name__
    my_fun
    >>> 

       最后需要指出,由于我们把原函数签名改成了(*args, **kw),因此,无法获得原函数的原始参数信息。即便我们采用固定参数来装饰只有一个参数的函数

    四。参考

    https://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/001386819879946007bbf6ad052463ab18034f0254bf355000

    http://www.imooc.com/code/6067

    https://www.imooc.com/learn/581

  • 相关阅读:
    PTA9
    PTA8
    第七周
    第六周
    第五周
    PTA4
    2019第三次作业
    第十周课程总结
    第九周课程总结&实验报告(七)
    第八周课程总结&实验报告(六)
  • 原文地址:https://www.cnblogs.com/GH-123/p/7787163.html
Copyright © 2011-2022 走看看