记录我学习《流畅的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)
综上,闭包是一种函数,它会保留定义函数时存在的自由变量的绑定,这样调用函数时,虽然定义作用域不可用了,但是仍能使用那些绑定。