• 函数装饰器和闭包


    装饰器的作用

      函数装饰器用于在源码中“标记”函数,以某种方式增强函数的行为。

      装饰器是可调用对象,参数是另一个函数(被装饰的函数)。

      装饰器可能会处理被装饰的函数,然后把它返回,或者将其替换成另一个函数或可调用对象。

    装饰器的特(1)把被装饰的函数替换成其他函数。(2)装饰器在加载模块时立即执行

    变量作用域规则:Python不要求声明变量,但是假定在函数定义体中赋值的变量是局部变量。

    闭包:延伸了作用域的函数。包含函数定义体中引用,同时还可以访问定义体之外定义的非全局变量。

    def make_average():
        series = []
        
        def averager(new_value):
            series.append(new_value)
            total = sum(series)
            return total/len(series)
        
        return average

    >>> avg = make_average()
    >>> avg(10)

      在averager函数中,series是自由变量free variable)指未在本地作用域中绑定的变量。

      Python在 __code__ 属性中保存 局部变量自由变量的名称。

    >>> avg.__code__.co_varname
    ('new_value','total')
    >>> avg.__code__.co_freevars
    ('series',)
    

      series的绑定在返回的avg对象的__closure__属性中。

      avg.__closure__中的各个元素对应于avg.__code__.co_freevars中的一个名称。这些元素是cell对象。

      它们的值保存在cell_contents属性中。

    >>> avg.__code__.co_freevars
    ('series',)
    >>> avg.__closure__
    (<cell at 0x000001C2EBE261C8: list object at 0x000001C2EBE33988>,)
    >>> avg.__closure__[0].cell_contents
    [10,]
    

      nonlocal声明:把变量标记为自由变量。

    def make_averager():
        count = 0
        total = 0
        def averager(new_value):
            nonlocal count,total
            count += 1
            total += new_value
            return total/count
    
        return average
    

      当变量是数字或任何不可变类型时,averager的定义体中为count赋值,实质会隐式创建局部变量count。

      引入nonlocal声明,把变量标记为自由变量,即使在函数中为变量赋予新值,也会变成自由变量。

      

    标准库中的装饰器

    functools.lru_cache 实现了备忘功能。把耗时的函数结果保存起来,避免传入相同的参数时重复计算。

    递归函数适合使用lru_cache。

    functools.lru_cache(maxsize=128,typed=Fales)

    maxsize参数指定存储多少个调用的结果。缓存满了后,旧得结果会被扔掉。maxsize应该设置为2的幂

    typed参数为True,不同参数类型得到的结果分开保存。

    ▲ lru_cache使用字典存储结果,键根据调用时传入的定位参数和关键字参数创建,所以被装饰的函数所有参数必须是可散列的。

    functools.singledispatch 装饰器可以把整体方案拆分成多个模块。

    根据第一个参数的类型,以不同的方式执行相同操作的一组函数。

    from functools import singledispatch
    from collections import abc
    import numbers
    
    @singledispatch
    def inp_check(obj):
        print('Main inp_check')
    
    @inp_check.register(str)
    def _(text):
        print('Text %s'%text)
    
    @inp_check.register(numbers.Integral)
    def _(n):
        print('Int %s' %n)
    
    @inp_check.register(tuple)
    @inp_check.register(abc.MutableSequence)
    def _(seq):
        print('List %s'%seq)
    

    注册的专门函数应该处理抽象基类(numbers.Integral和abc.MutableSequence),不需处理具体实现(int、list)。

    这样,代码支持的兼容类型更广泛。

    参数化装饰器

      Python把被装饰的函数作为第一个参数传递给装饰器函数,那么如何给装饰器传递其他参数呢?

    def outer(func):
        def inner(*args,**kwargs):
            
            print('Do Something..')
            result = func(*args,**kwargs)
            return result
        
        return inner
    
    @outer
    def f1(num):
        print('Number %s'%num)
    
    # ========= 等效于
    f1 = outer(f1)

      我们可以创建一个装饰器工厂函数,把参数传给它,返回一个装饰器,然后再把它运用到要装饰的函数上。

    def register(active=False):
        
        def outer(func):
            def inner(*args,**kwargs):
                print('Do Something..')
                result = func(*args,**kwargs)
                return result
            return inner
        
        return outer
    
    @register(active=True)
    def f1(num):
        print('Number %s'%num)
    
    # =========等效于
    f1 = register(active=True)(f1)
    

      

  • 相关阅读:
    js中的回调函数的理解和使用方法
    js循环的总结
    jquery选择器
    Jquery的命名冲突
    ul+li标签制作表格
    MyEclipse代码提示功能和自动提示功能
    a configuration error occured during startup.please verify the preference field with the prompt:
    MyEclipse2014,java文件无法编译,run as上是none applicable,不是文件本身的问题
    Myeclipse自定义注释
    Run As none applicable
  • 原文地址:https://www.cnblogs.com/5poi/p/11193393.html
Copyright © 2020-2023  润新知