1.装饰器基本知识 装饰器是一个可调用的对象 @decorate def target(): print(‘running target()’)
和 def target(): print(‘running target()’) target = decorate(target) 这两种写法一样
2.装饰器通常把函数替换成另一个函数 def deco(func): def inner(): print(‘running inner()’) return inner
@deco def target(): print(‘running target()’) 运行target()会发现其实在运行inner
3.装饰器在被装饰的函数定义后立即运行 通常在导入时就运行了
registry = [] def register(func): print(‘running register(%s)’ % func) return func
@register def f1(): print(‘running f1’)
def f3(): print(‘running f3’)
print(‘running main()’) f1() f3()
最终结果为>>> running register(<function f1 at 0x100631bf8>) running register(<function f1 at 0x100631c80>) running main() running f1() running f3 装饰器在定义函数时就运行了
4.使用装饰器 改进策略模式 上一节中提到的选择最优策略 best_promo 有一个列表存放策略 但是可能会添加了新的策略但是没有加入列表中 从而导致难以发现的问题 改进:利用装饰器 promos = [] def promotion(promo_func): promos.append(promo_func) return promo_func
@promotion def fidelity(order): return order.total() * .05 if order.customer.fidelity >= 1000 else 0
@promotion def bulk_item(order): pass
@promotion def large_order(order): pass
5.变量作用域规则 在函数中调用一个变量 如果在局部没有定义 会自动在全局中寻找并作为全局变量使用 但如果有定义的语句 则不管在定义语句的前后 这个变量都一定是局部变量 不会去寻找匹配的全局变量 也就是说 在定义之前使用就会出错
6.闭包 闭包和匿名函数不同 闭包指延伸了作用域的函数 例如实现avg()求平均值 但是可以计算不断增加的系列之的均值
avg(10) 10
avg(11) 10.5
avg(12) 11
实现方法一 类实现 class Average(): def init(self): self.series = [] def call(self, new_values): self.series.append(new_values) total = sum(self.series) return total/len(self.series)
avg = Average()#avg是Average的实例 avg(10) avg(11)
实现方法二 高阶函数make_average def make_average(): series = []
def average(new_value):
series.append(new_value)
total = sum(series)
return total/len(series)
return average
avg = make_average()#avg是内部函数average avg(10) avg(11) 本来在调用avg时 函数make_avergae早就返回了 作用域也消失了 所以在average函数中series 时自由变量 series = []和后面的def average 形成了闭包 延伸到函数作用域之外了 所以闭包是一种函数 会保留定义函数时存在的自由变量的绑定
7.第6条中的series是列表 函数中的函数修改它时没有把它作为局部变量来看 但如果series是一个整型值 在函数中的函数执行series += 1 相当于定义,也就是有一个隐性的series局部变量 这时会出错 不在作为自由变量看待 可以加入 nonlocal series 来声明这不是局部变量
8.实现装饰器 输出函数的运行时间 (装饰器的特点 接受相同的参数 代替原来的函数 返回本该返回的值 同时还做些额外操作) import time
def clock(func): def clocked(*args):#可以接受 任意个定位参数 t0 = time.perf_counter() result = func(*args) elapsed = time.perf_counter() - t0 name = func.name arg_str = ‘, ‘.join(repr(arg) for arg in args) print(’[%0.8fs] %s(%s) -> %r’ % (elapsed, name, arg_str, result)) return result return clocked#取代被装饰的函数
9.以上的clock装饰器有些缺点: 不支持关键字参数 遮盖了被装饰函数的__name__和__doc__属性 改进 import time import functools #使用了functools里的wraps 来把相关属性赋值给clocked
def clock(func): @functools.wraps(func) def clocked(*args, **kwargs): t0 = time.time() result = func(*arg, **kwargs) elapsed = time.time() - to name = func.name arg_lst = [] if args : arg_lst.append(’.’.join(repr(arg) for arg in args)) if kwargs: pairs = [’%s=%r’ % (k,w) for k, w in sorted(kwargs.items())] arg_lst.append(’, '.join(pairs)) arg_str = ', ‘.join(arg_lst) print(’[%0.8fs] %s(%s) -> %r ’ % (elapsed, name, arg_str, result)) return result return clocked
10.标准库的装饰器 property,classmethod,staticmethod 以后会讨论的
11.加入functools.lru_cache做备忘 例 @clock def fibonacci(n): if n < 2: return n return fibonacci(n-2) + fibonacci(n-1) fibonacci(6) 改进 import functools @functools.lru_cache() @clock def fibonacci(n): if n < 2: return n return fibonacci(n-2) + fibonacci(n-1) 结果在P169
- 单分派泛函数
使用@ingledispatch把多个函数绑定在一起组成一个泛函数 例 @singleddispatch def htmlize(obj): content = html.escape(repr(obj)) return ‘{}’.format(content)
@htmlize.register(str) def _(text): pass
@htmlize.register(numbers.Intergral) def _(n): pass
13.叠放装饰器 @d1 @d2 def f(): pass 等同于 def f(): pass f = d1(d2(f))
14.参数化装饰器 使得装饰器可以接受参数 例 registry = set()
def register(active = True): def decorate(func): if active: registry.add(func) else: registry.discard(func) return func return decorate
@register(active = False) def f1(): pass
@register() def f2(): pass
15.参数化之前的clock装饰器 def clock(fmt=DEFAULT_FMT): def decorate(func): def clocked(*args):#可以接受 任意个定位参数 t0 = time.perf_counter() result = func(*args) elapsed = time.perf_counter() - t0 name = func.name arg_str = ‘, ‘.join(repr(arg) for arg in args) print(’[%0.8fs] %s(%s) -> %r’ % (elapsed, name, arg_str, result)) return result return clocked return decorate
|