• python的装饰器和闭包


    函数装饰器和闭包

    1装饰器前提:

    函数装饰器用于在源码中“标记”函数,以某种方式增强函数的行为。这是一项强大的功能,但是若想掌握,必须理解闭包。
    nonlocal 是新近出现的保留关键字,在 Python 3.0 中引入。作为 Python 程序员,如果严格遵守基于类的面向对象编程方式,即便不知道这个关键字也不会受到影响。然而,如果你
    想自己实现函数装饰器,那就必须了解闭包的方方面面,因此也就需要知道 nonlocal 。
     

     
     
     
     
     
     
     
     
     
     
     
    1
    自定义一个装饰器
    2
    
    
    3
    >>> def deco(func):
    4
    ...     def inner():
    5
    ...             print('running inner()')
    6
    ...     return inner
    7
    ...
    8
    >>> @deco
    9
    ... def target():
    10
    ...     print('running target()')
    11
    ...
    12
    >>> target()
    13
    running inner()
    14
    >>> target
    15
    <function deco.<locals>.inner at 0x7fd604acd1e0>
    16
    
    
    17
    
    
     
     

     
     
     
     
     
     
     
     
     
     
     
    1
    """
    2
    练习装饰器
    3
    2019年11月16日
    4
    作者:戴昊龙
    5
    """
    6
    regis = []
    7
    
    
    8
    
    
    9
    def registry(func):
    10
        print("running registry %s" % func)
    11
        regis.append(func)
    12
        return func
    13
    
    
    14
    
    
    15
    @registry
    16
    def func1():
    17
        print("running func1")
    18
    
    
    19
    
    
    20
    @registry
    21
    def func2():
    22
        print("running func2")
    23
    
    
    24
    
    
    25
    def func3():
    26
        print("running func3")
    27
    
    
    28
    
    
    29
    def main():
    30
        print("running main")
    31
        print("regis---->", regis)
    32
        func1()
    33
        func2()
    34
        func3()
    35
    
    
    36
    
    
    37
    if __name__ == '__main__':
    38
        main()
    39
    
    
     
     
     

     
     
     
     
     
     
     
     
     
     
     
    1
    running registry <function func1 at 0x0000000002207A60>
    2
    running registry <function func2 at 0x0000000002207EA0>
    3
    running main
    4
    regis----> [<function func1 at 0x0000000002207A60>, <function func2 at 0x0000000002207EA0>]
    5
    running func1
    6
    running func2
    7
    running func3
    8
    
    
     
     

    2、变量的作用域


     
     
     
     
     
     
     
     
     
     
     
    1
    >>> b = 6
    2
    >>> def test():
    3
    ...     a = 9
    4
    ...     print(a)
    5
    ...     print(b)
    6
    ...     b = 10
    7
    ...
    8
    >>> test()
    9
    9
    10
    Traceback (most recent call last):
    11
      File "<stdin>", line 1, in <module>
    12
      File "<stdin>", line 4, in test
    13
    UnboundLocalError: local variable 'b' referenced before assignment
    14
    
    
     
     
    上面这个例子看起来很奇怪,但是实际上,Python 编译函数的定义体时,它判断 b 是局部变量,因为在函数中给它赋值了。生成的字节码证实了这种判断,Python 会尝试从本地环境获取 b 。后面调用 test 时,test 的定义体会获取并打印局部变量 a 的值,但是尝试获取局部变量 b 的值时,发现 b 没有绑定值。

     
     
     
     
     
     
     
     
     
     
     
    1
    >>> def f():
    2
    ...     a = 9
    3
    ...     print(a)
    4
    ...     global b
    5
    ...     print(b)
    6
    ...     b =10
    7
    ...
    8
    >>> f()
    9
    9
    10
    6
    11
    >>> b
    12
    10
     
     
    如果在函数中赋值时想让解释器把 b 当成全局变量,要使用 global 声明:
     

    3、闭包

    怎么理解闭包?有难度的一个概念:最肤浅的理解,就是函数里面定义函数
    在函数内部定义函数不常见,直到开始使用匿名函数才会这样做。而且,只有涉及嵌套函数时才有闭包问题。
    其实,闭包指延伸了作用域的函数,其中包含函数定义体中引用、但是不在定义体中定义的
    非全局变量。函数是不是匿名的没有关系,关键是它能访问定义体之外定义的非全局变量。
     

     
     
     
     
     
     
     
     
     
     
     
    1
    # 比方说一个avg函数,用来实现移动求平均
    2
    # 每次都叠加求平均
    3
    avg(10) ---- 10.0
    4
    avg(11)----- 10.5
    5
    avg(12)------11.0
    6
    
    
    7
    # 怎么来实现呢? 普通的应该就是用类和对象来实现,第二种方法可以用高阶函数来实现
    8
    # example(1),用类来实现
    9
    class Average():
    10
        def __init__(self):
    11
            self.series = []
    12
    
    
    13
        def __call__(self, new_value):
    14
            self.series.append(new_value)
    15
            total = sum(self.series)
    16
            return total/len(self.series)
    17
    
    
    18
    a1 = Average()
    19
    print(a1(10))
    20
    
    
    21
    # example(2), 用高阶函数实现
    22
    def make_averager():  # example2
    23
        series = []   # 这个就是闭包
    24
    
    
    25
        def average(num):
    26
            series.append(num)  # series是自由变量
    27
            total = sum(series)
    28
            return total/len(series)
    29
        return average  # 每次返回的是一个函数对象
    30
    
    
    31
    
    
    32
    a1 = make_averager()
    33
    print(a1(10))
    34
    print(a1(20))
    35
    
    
    36
    # example(3), 例2中,其实函数的效率不是很高,因为每次都在重复的求取sum,len, 这里我们可以改进一下
    37
    def make_average():
    38
        count = 0
    39
        total = 0
    40
    
    
    41
        def average(num):
    42
            nonlocal count, total   # 没有这句声明那这个example3就是错的,为什么呢?
    43
    """
    44
    数字、字符串、元组等不可变类型来说,只能读取,不能更新。如果尝试重新绑 
    45
    定,例如 count = count + 1 ,其实会隐式创建局部变量 count 。这样, count 就不是自由       怎么理解这段话?
    46
    变量了,因此不会保存在闭包中。 
    47
                                 当i = 10的时候。 实际上 i += 1 并不是真的在原有的int对象上+1,而是重新创建一个value为 11 的int对象,i引用自这个新的对象。
    48
    
    
    49
    """
    50
            count += 1
    51
            total = total + num
    52
            return total/count
    53
        return average
    54
    """
    55
    nonlocal 它的作用是把变量标记为自由变量,
    56
    即使在函数中为变量赋予新值了,也会变成自由变量。如果为 nonlocal 声明的变量赋予新值,闭包中保存的绑定会更新。
    57
    """
     
     
    闭包是一种函数,它会保留定义函数时存在的自由变量的绑定,这样调用函数时,虽然定义作用域不可用了,但是仍能使用那些绑定。
    注意,只有嵌套在其他函数中的函数才可能需要处理不在全局作用域中的外部变量。
     

    4、 正式开始装饰器

    前面的都是准备工作, 这个部分先不学习,先搞懂装饰器的基础知识
     
    Nobody knows it better than me.
  • 相关阅读:
    仿msn风格的选项卡_网页代码站(www.webdm.cn)
    拍拍网头部导航菜单_网页代码站(www.webdm.cn)
    横向滑动特效的菜单(js+css)_网页代码站(www.webdm.cn)
    QuickMenu 超强悍菜单_网页代码站(www.webdm.cn)
    紫罗兰风格的导航条_网页代码站(www.webdm.cn)
    兼容性超强的六级网站导航主菜单_网页代码站(www.webdm.cn)
    用JavaScript实现横向滑出的多级竖向菜单_网页代码站(www.webdm.cn)
    一款溢出式侧边栏导航菜单_网页代码站(www.webdm.cn)
    css打造的又一款清新的横排下拉菜单_网页代码站(www.webdm.cn)
    [ZZ]byte[]到short、int、long的相互转换
  • 原文地址:https://www.cnblogs.com/dadaizi/p/13060432.html
Copyright © 2020-2023  润新知