python3 闭包和装饰器

闭包

  • 闭包值得是延伸了作用域的函数,其中包含函数定义体中的引用,但是不在定义体中定义的非全局变量

例子1

def make_avg():
    series = []     #自由变量       ---
    def ave(n):                     #|
        series.append(n)            #此间范围为闭包
        total = sum(series)         #|
        return total/len(sries)     ---
    return ave
#控制台输入:
a = make_avg()
a(10)
a(11)

上例的方法效率不高,要把所有值存在历史记录里面,更好的方式是存储当前值和长度

例子2

def make_ave():
    count=0
    res = 0
    
    def aver(n):
        count += 1
        res += n
        return res / count
    return aver

例子2在spyder提示错误无法编译,当count是数字或任何不可变的类型时,count+=1语句的作用其实与count=count+1一样,一次在aver中为count赋值了,会把count变成局部变量,所以count就不是自由变量了,不会保存在闭包中。而例子1没问题是因为调用append,并把它传给sum和len。

  • 解决方法:加nonlocal
def make_ave():
    count=0
    res = 0
    
    def aver(n):
        nonlocal count, res
        count += 1
        res += n
        return res / count
    return aver

装饰器

实现简单的装饰器
有时候我们觉得这个函数不够完善,我想在这个函数的基础上拓展一些额外的通用的功能,但是由于这个函数又很重要,不能直接侵入代码去改,加上可能其它的函数也需要这样一种功能,就是这样一个@xxx的东西

#简单的支持多参数装饰器  
def deco(fun):
    def wap(*args, **kargs):
        time_start = time.time()
        fun(*args, **kargs)
        time_end = time.time()
        print('running time :', time_end-time_start)
    return wap

@deco
def Afun1(a, b,c):
    time.sleep(1)
    print('a+b+c=',a+b+c)
Afun1(1,3,4) 
#7.7
'''
实现简单的装饰器
'''
import time 
def clock(f):
    def clocked(*args):
        t0 = time.perf_counter()
        result = f(*args)
        ela = time.perf_counter() - t0
        name = f.__name__
        arg_str = ', '.join(repr(arg) for arg in args)
        print('[%0.8fs] %s(%s) ->%r' % (ela, name, arg_str, result))
        return result
    return clocked

@clock
def snooze(sec):
    time.sleep(sec)

@clock
def fact(n):
    return 1 if n < 2 else n*fact(n-1)

if __name__=='__main__':
    print('*'*40, 'calling snooze(.123)')
    snooze(.123)
    print('*'*40, 'calling snooze(6)')
    fact(6)

其中:

上例代码等价于:
def snooze(sec):
    time.sleep(sec)
clock(sbooze)

fact 保存的是clocked函数的引用,每次调用fact(n),执行的都是clocked(n),clocked大致做了下面几件事情:

  • 记录初始时间t0
  • 调用原来的fact函数,保存结果
  • 计算经过的时间
  • 格式化收集的数据
  • 返回第二步的结果

标准库的装饰器

  • functools.lru_cache
  • singledispatch

functools.lru_cache

  • 实现了备忘功能,把耗时的函数结果保存起来

例子3

@clock
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n-2) + fibonacci(n-1)

print(fibonacci(6))
#此代码非常耗时间,有很多重复计算

改良:

import functools

@functools.lru_cache()
@clock
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n-2) + fibonacci(n-1)
    
print result:
[0.00000030s] fibonacci(0) ->0
[0.00000030s] fibonacci(1) ->1
[0.00002180s] fibonacci(2) ->1
[0.00000060s] fibonacci(3) ->2
[0.00004260s] fibonacci(4) ->3
[0.00000040s] fibonacci(5) ->5
[0.00006430s] fibonacci(6) ->8
8
  • 注意:lru_cache可以使用两个可选的参数来配置。
lru_cache(maxsize=128, typed = False)

maxsize指定存储多少调用结果,为2的次幂。typed参数如果设置为True,把不同参数分开保存。字典存储结果,所以参数必须可散列的。

例子1 实现的clock装饰器缺点:不支持关键字,遮盖了呗装饰的_name__和__doc__属性,使用functools.wraps()可以解决:

def clock(func):
    @functools.wraps(func)
    def clock(*args, **kargs):
    

单分派泛函数

python不支持重载方法函数,所以使用singledispatch装饰器把整体方案拆分成多个模块。

from functools import singledispatch
from collections import abc
import numbers
import html

@singledispatch   #标记处理object类型的基函数
def htmlize(obj):
    content = html.escape(repr(obj))
    return '<pre>{}</pre>'.format(content)

@htmlize.register(str)
def _(text): #专用名称函数无关紧要
    content = html.escape(text).replace('\n','<br>\n')
    return '<p>{0}</p>'.format(content)
    
@htmlize.register(numbers.Integral)
def _(n):
    return '<pre>{0}(0x{0:x})</pre>'.format(n)
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值