亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定

函數裝飾器和閉包

標簽:
Python

 

装饰器基础知识

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

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

复制代码

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

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

输出:

inner is called
<function deco.<locals>.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(<function func1 at 0x000001BAE35B9EA0>)
running register(<function func2 at 0x000001BAE35B9F28>)
main is running
registry: [<function func1 at 0x000001BAE35B9EA0>, <function func2 at 0x000001BAE35B9F28>]
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 '<pre>{}<pre>'.format(html.escape(repr(obj)))# 各个专门函数使用@<base_function>.register(<type>)修饰@htmlize.register(str)# 函数名无关紧要,_是个不错的选择,简单明了def _(txt):
    s = html.escape(txt).replace('\n','<br>\n')    return '<p>{}<p>'.format(s)# numbers.Integral是int的虚拟超类@htmlize.register(numbers.Integral)def _(n):    return '<pre>{0}(0x{0:X})</pre>'.format(n)# 可以叠放多个register装饰器,让同一个函数支持不同类型@htmlize.register(tuple)
@htmlize.register(abc.MutableSequence)def _(seq):
    inner = '</li>\n<li>'.join(htmlize(item) for item in seq)
    outer = '<ul>\n<li>' + inner + '</li>\n</ul>'
    return outerprint(htmlize({1, 2, 3}))print(htmlize(abs))print(htmlize(42))print(htmlize(['alpha',66,{3,2,1}]))

输出:

<pre>{1, 2, 3}<pre>
<pre>&lt;built-in function abs&gt;<pre>
<pre>42(0x2A)</pre>
<ul>
<li><p>alpha<p></li>
<li><pre>66(0x42)</pre></li>
<li><pre>{1, 2, 3}<pre></li>
</ul>

复制代码

 

一个参数化的装饰器

复制代码

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(<function func1 at 0x00000201B1579F28>)
running register(active=True) -> decorate(<function func2 at 0x00000201B1586048>)
running register(active=False) -> decorate(<function func3 at 0x00000201B15860D0>)
running func1
running func2
running func3
{<function func1 at 0x00000201B1579F28>, <function func2 at 0x00000201B1586048>}

复制代码

 

三层嵌套的装饰器

复制代码

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  

點擊查看更多內容
TA 點贊

若覺得本文不錯,就分享一下吧!

評論

作者其他優質文章

正在加載中
  • 推薦
  • 評論
  • 收藏
  • 共同學習,寫下你的評論
感謝您的支持,我會繼續努力的~
掃碼打賞,你說多少就多少
贊賞金額會直接到老師賬戶
支付方式
打開微信掃一掃,即可進行掃碼打賞哦
今天注冊有機會得

100積分直接送

付費專欄免費學

大額優惠券免費領

立即參與 放棄機會
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號

舉報

0/150
提交
取消