记录学习《流畅的python》的一些知识-----函数装饰器和闭包

记录我学习《流畅的python》的过程

2021.6.2

1.装饰器基础知识
装饰器用于在源码中“标记”函数,以某种方式增强函数的行为。
装饰器是可调用的对象,其参数是另一个函数(被装饰的函数)。
装饰器可能会处理被装饰的函数,然后把它返回,或者将其替换成另一个函数或可调用对象。

一个名叫decorate的装饰器:

@decorate
def target():
    print('running target()')



def target():
    print('running target()')
    
target = decorate(target)

两种写法的最终结果一样:上述两个代码片段执行完毕后得到的target不一定是原来那个target函数,而是decorate(target)。

确认被装饰的函数会被替换,看控制台:

def deco(func):
    def inner():
        print('running inner()')
    return inner

@deco
def target():
    print('running target()')

print(target())
print(target)

在这里插入图片描述
审查对象,发现target现在是inner的引用。

2.Python何时执行装饰器
装饰器的一个关键特性是,它们在被装饰的函数定义之后立即运行。
registration.py模块

registry = []

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

@register
def f1():
    print('running f1()')

@register
def f2():
    print('running f2()')

def f3():
    print('running f3()')
    
def main():
    print('running main()')
    print('registry ->', registry)
    f1()
    f2()
    f3()

if __name__=='__main__':
    main()

运行结果:
在这里插入图片描述

函数装饰器在导入模块时立即执行,而被装饰的函数旨在明确调用时运行,这突出了Python程序员所说的导入时和运行时之间的区别。

3.使用装饰器改进”策略“模式
promos列表中的值使用promotion装饰器填充

promos = []
def promotion(promo_func):
    promos.append(promo_func)
    return promo_func

@promotion
def fidelity(order):
    """为积分为1000或以上的顾客提供5%折扣"""
    return order.total() * .05 if order.customer.fidelity >= 1000 else 0

@promotion
def bulk_item(order):
    """单个商品为20个或以上时提供10%折扣"""
    discount = 0
    for item in order.cart:
        if item.quantity >= 20:
            discount += item.total() * .1
    return discount

@promotion
def large_order(order):
    """订单中的不同商品达到10个或以上时提供7%折扣"""
    distinct_items = {item.product for item in order.cart}
    if len(distinct_items) >= 10:
        return order.total() * .07
    return 0

def best_promo(order):
    """选择可用的最佳折扣"""
    return max(promo(order) for promo in promos)

优点:
促销策略函数无需使用特殊的名称(即不用以_promo结尾)
@promotion装饰器突出了被装饰的函数的作用,还便于临时禁用某个促销策略:只需把装饰器注释掉。
促销折扣策略可以在其他模块中定义,在系统中的任何地方还行,只要使用@promotion装饰即可。

4.变量作用域规则
定义并测试了一个函数,它读取两个变量的值:一个是局部变量a,是函数的参数;另一个是变量b,这个函数没有定义它。

def f1(a):
    print(a)
    print(b)

print(f1(3))

在这里插入图片描述
如果先给全局变量b赋值,然后在调用f,那就不会出错

def f1(a):
    print(a)
    print(b)
    
b=6
print(f1(3))

在这里插入图片描述

如果在函数体中对b进行赋值,那么b就会变成局部变量。

b=6
def f2(a):
    print(a)
    print(b)
    b=9

print(f2(3))

在这里插入图片描述
如果在函数中赋值时想让解释器把b当成全局变量,要使用global声明:

b=6
def f2(a):
    global b
    print(a)
    print(b)
    b=9

print(f2(3))

print(b)

print(f2(3))

b=30
print(b)

在这里插入图片描述
5.闭包
在博客圈,人们有时会把闭包和匿名函数弄混。这是有历史原因的:在函数内部定义函数不常见,直到开始使用匿名函数才会这样做。而且,只有涉及嵌套函数时才有闭包问题。因此,很多人是同时知道这两个概念的。
计算移动平均值的类

class Averager():

    def __init__(self):
        self.series = []

    def __call__(self, new_value):
        self.series.append(new_value)
        total = sum(self.series)
        return total/len(self.series)

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

在这里插入图片描述
计算平均值的高阶函数

def make_averager():
    series = []

    def averager(new_value):
        series.append(new_value)
        total = sum(series)
        return total/len(series)

    return averager

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

在这里插入图片描述
审查make_averager创建的函数

print(avg.__code__.co_varnames)
print(avg.__code__.co_freevars)
print(avg.__closure__)
print(avg.__closure__[0].cell_contents)

在这里插入图片描述
综上,闭包是一种函数,它会保留定义函数时存在的自由变量的绑定,这样调用函数时,虽然定义作用域不可用了,但是仍能使用那些绑定。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值