栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

函数装饰器和闭包

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

函数装饰器和闭包

 

装饰器基础知识

  - 装饰器是可调用对象,其参数是另一个函数

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

def deco(func):    def inner():        print("inner is called")    return inner

@decodef func():    print("func is called")# func被装饰器修改成innerfunc()print(func)

输出:

inner is called
.inner at 0x00000155417B9F28>

 

Python何时执行装饰器

  - 装饰器的一个关键特性是,它们在被装饰的函数定义之后立即运行。

  - 即在模块导入初始化时运行

registry = []def register(func):    print('running register(%s)' % func)
    registry.append(func)    return func

@registerdef func1():    print('func1 is running')

@registerdef func2():    print('func2 is running')def func3():    print('func3 is running')def main():    # 在main()运行之前,register已经运行了两次
    # 这两次就是在被装饰函数定义时
    # 所以registry也已经被初始化
    print('main is running')    print('registry:',registry)
    func1()
    func2()
    func3()if __name__ == '__main__':
    main()

输出:

running register()
running register()
main is running
registry: [, ]
func1 is running
func2 is running
func3 is running

 

变量作用域规则

  - Python不要求声明变量,但是假定在函数定义体中有赋值的变量是局部变量

b = 3def f1(a):    print(a)    # 这里b是局部变量,所以错误提示为在赋值之前使用
    # 如果想获取全局变量b,需要加上global b
    print(b)
    b = 9f1(1)

 

闭包

  - 只有涉及嵌套函数时才有闭包问题

  - 闭包指延伸了作用域的某个函数,它包含了不在函数内定义的非全局变量

def make_avg():
    vars = []    def avg(value):
        vars.append(value)
        total = sum(vars)        return total/len(vars)    return avg# avg中包含了在make_avg中定义的vars列表# 即使make_avg已经返回,但vars仍然被保留下来avg = make_avg()print(avg(10))print(avg(11))print(avg(12))# 闭包中引用的非自己定义的的非全局变量称为自由变量# 自由变量的名称被保存在__code__.co_freevars中# 自由变量的值被保存在__closure__[i].cell_contents中print('avg.__code__.co_freevars:',avg.__code__.co_freevars)print('avg.__closure__[0].cell_contents',avg.__closure__[0].cell_contents)

输出:

10.0
10.5
11.0
avg.__code__.co_freevars: ('vars',)
avg.__closure__[0].cell_contents [10, 11, 12]

 

nonlocal声明

  - 在闭包中对自由变量进行赋值,会生成一个局部变量覆盖自由变量,并且报错:局部变量使用前未赋值

def make_avg():
    count=0
    total=0    def avg(value):        # count+=1就是count = count + 1
        # 这里会自动生成一个未赋初始值的局部变量count覆盖自由变量count
        count+=1        # total和count相同情况
        total+=value        return total/count    return avg

avg = make_avg()print(avg(10))print(avg(11))print(avg(12))

输出:
UnboundLocalError: local variable 'count' referenced before assignment

  - 使用nonlocal声明,不会覆盖自由变量

def make_avg():
    count=0
    total=0    def avg(value):        # 使用nonlocal声明,不会覆盖自由变量        nonlocal count,total
        count+=1
        total+=value        return total/count    return avg

avg = make_avg()print(avg(10))print(avg(11))print(avg(12))

输出:10.0
10.5
11.0

 

一个函数执行时间的装饰器

   - func的函数__name__和__doc__属性将被clocked相应属性覆盖

import timedef clock(func):    def clocked(*args):
        t0 = time.perf_counter()
        result = func(*args)
        elapsed = time.perf_counter() - t0
        func_name = func.__name__
        func_args = ','.join(str(args))
        s = '[%0.8fs] %s(%s) --> %r' % (elapsed,func_name,func_args,result)        print(s)        return result    return clocked

@clockdef snooze(n):
    time.sleep(n)

@clockdef factorial(n):    if n == 1:        return 1    else:        return n*factorial(n-1)

snooze(0.123)print('-' * 100)print('factorial(6):',factorial(6))print('-' * 100)print('the name of factorial:',factorial.__name__)

输出:
[0.12324016s] snooze((,0,.,1,2,3,,,)) --> None----------------------------------------------------------------------------------------------------[0.00000032s] factorial((,1,,,)) --> 1[0.00001091s] factorial((,2,,,)) --> 2[0.00002855s] factorial((,3,,,)) --> 6[0.00003401s] factorial((,4,,,)) --> 24[0.00003914s] factorial((,5,,,)) --> 120[0.00004588s] factorial((,6,,,)) --> 720factorial(6): 720
----------------------------------------------------------------------------------------------------the name of factorial: clocked

   - 使用wraps装饰器后,func的__name__和__doc__将不会被覆盖

import timeimport functoolsdef clock(func):    # 使用wraps装饰器后,func的__name__和__doc__将不会被覆盖    @functools.wraps(func)    def clocked(*args):
        t0 = time.perf_counter()
        result = func(*args)
        elapsed = time.perf_counter() - t0
        func_name = func.__name__
        func_args = ','.join(str(args))
        s = '[%0.8fs] %s(%s) --> %r' % (elapsed,func_name,func_args,result)        print(s)        return result    return clocked

@clockdef factorial(n):    if n == 1:        return 1    else:        return n*factorial(n-1)print('the name of factorial:',factorial.__name__)

输出:
the name of factorial: factorial

 

使用functools.lru_cache做备忘

import timeimport functoolsdef clock(func):    # 使用wraps装饰器后,func的__name__和__doc__将不会被覆盖    @functools.wraps(func)    def clocked(*args):
        t0 = time.perf_counter()
        result = func(*args)
        elapsed = time.perf_counter() - t0
        func_name = func.__name__
        func_args = ','.join([str(a) for a in args if str(a) != ''])
        s = '[%0.8fs] %s(%s) --> %r' % (elapsed,func_name,func_args,result)        print(s)        return result    return clocked# 不使用lru_cache,fib(6)要调用fib(1)8次,fic(2)5次等等@clockdef fib(n):    if n < 2:        return n    else:        return fib(n-2) + fib(n-1)# 使用lru_cache,可以缓存中间结果,避免重复调用。@functools.lru_cache()
@clockdef fib2(n):    if n < 2:        return n    else:        return fib2(n-2) + fib2(n-1)print('fib(6):',fib(6))print('-' * 100)print('fib2(6):',fib2(6))

输出:
[0.00000032s] fib(0) --> 0
[0.00000032s] fib(1) --> 1[0.00004235s] fib(2) --> 1[0.00000000s] fib(1) --> 1[0.00000000s] fib(0) --> 0
[0.00000032s] fib(1) --> 1[0.00001059s] fib(2) --> 1[0.00002117s] fib(3) --> 2[0.00007443s] fib(4) --> 3[0.00000032s] fib(1) --> 1[0.00000032s] fib(0) --> 0
[0.00000000s] fib(1) --> 1[0.00001091s] fib(2) --> 1[0.00002181s] fib(3) --> 2[0.00000032s] fib(0) --> 0
[0.00000000s] fib(1) --> 1[0.00001123s] fib(2) --> 1[0.00000032s] fib(1) --> 1[0.00000032s] fib(0) --> 0
[0.00000032s] fib(1) --> 1[0.00001123s] fib(2) --> 1[0.00002246s] fib(3) --> 2[0.00004459s] fib(4) --> 3[0.00007731s] fib(5) --> 5[0.00016297s] fib(6) --> 8fib(6): 8
----------------------------------------------------------------------------------------------------[0.00000000s] fib2(0) --> 0
[0.00000032s] fib2(1) --> 1[0.00001476s] fib2(2) --> 1[0.00000064s] fib2(3) --> 2[0.00002695s] fib2(4) --> 3[0.00000064s] fib2(5) --> 5[0.00003882s] fib2(6) --> 8fib2(6): 8

 

使用单分派函数

  - 类似其他面向对象语言的方法重载,以不同方式执行相同操作的一组函数

from functools import singledispatchimport htmlfrom collections import abcimport numbers# 单分派函数的基函数@singledispatchdef htmlize(obj):    return '
{}
'.format(html.escape(repr(obj)))# 各个专门函数使用@.register()修饰@htmlize.register(str)# 函数名无关紧要,_是个不错的选择,简单明了def _(txt):
    s = html.escape(txt).replace('n','
n')    return '

{}

'.format(s)# numbers.Integral是int的虚拟超类@htmlize.register(numbers.Integral)def _(n):    return '

{0}(0x{0:X})
'.format(n)# 可以叠放多个register装饰器,让同一个函数支持不同类型@htmlize.register(tuple) @htmlize.register(abc.MutableSequence)def _(seq):     inner = 'n
  • '.join(htmlize(item) for item in seq)     outer = '
      n
    • ' + inner + '
    • n
    '     return outerprint(htmlize({1, 2, 3}))print(htmlize(abs))print(htmlize(42))print(htmlize(['alpha',66,{3,2,1}])) 输出:
  • {1, 2, 3}

    <built-in function abs>

    42(0x2A)


    • alpha


    • 66(0x42)

    • {1, 2, 3}

     

    一个参数化的装饰器

    registry = set()# register是装饰器工厂,返回一个装饰器# 它接受一个可选关键字参数def register(active=True):    # decorate是真正的装饰器,它的参数是一个函数
        def decorate(func):        print('running register(active=%s) -> decorate(%s)' % (active,func))        # active为真,注册func
            if active:
                registry.add(func)        # active为假,删除func
            else:
                registry.discard(func)        return func    return decorate# register必须作为函数调用,并且可以传入参数@register(active=True)def func1():    print('running func1')# 即使不传入参数,也要作为函数调用@register()def func2():    print('running func2')# active为假,不注册func3@register(active=False)def func3():    print('running func3')if __name__ == '__main__':
        func1()
        func2()
        func3()    print(registry)
    
    输出:
    running register(active=True) -> decorate()
    running register(active=True) -> decorate()
    running register(active=False) -> decorate()
    running func1
    running func2
    running func3
    {}

     

    三层嵌套的装饰器

    import time
    
    default_fmt = '[{elapsed:0.8f}s] {name}({args}) -> {result}'# 一个三层嵌套的装饰器def clock(fmt=default_fmt):    # @clock()返回decorate
        def decorate(func):        # 再把被装饰函数传递给decorate,运行它
            # 返回clocked,替代被装饰函数
            def clocked(*args):
                t0 = time.time()
                _result = func(*args)
                elapsed = time.time()-t0
                name = func.__name__
                args = ','.join(repr(item) for item in args)
                result = repr(_result)            print(fmt.format(**locals()))            return _result        return clocked    return decorate# func被clocked替代@clock()def func():
        time.sleep(0.123)if __name__ == '__main__':    for i in range(3):
            func()
    
    输出:
    [0.12314367s] func() -> None
    [0.12322927s] func() -> None
    [0.12370729s] func() -> None

    作者:StackNeverOverFlow

    原文出处:https://www.cnblogs.com/StackNeverOverFlow/p/10453240.html  

    转载请注明:文章转载自 www.mshxw.com
    本文地址:https://www.mshxw.com/it/221523.html
    我们一直用心在做
    关于我们 文章归档 网站地图 联系我们

    版权所有 (c)2021-2022 MSHXW.COM

    ICP备案号:晋ICP备2021003244-6号