装饰器相关知识

装饰器相关知识

装饰器是一个可以调用的对象,接收的参数是一个函数,装饰器可以改变被装饰的函数,然后返回原函数或者其他可以调用的对象

将函数替换成另外的函数

1
2
3
4
5
6
7
8
9
10
def deco(func):
def test2():
print('hello, world')
return test2

@deco
def test1():
print("I'm the world!")

//result: hello, world

特性

  • 能把被装饰的函数替换成其他函数。
  • 装饰器在加载模块时立即执行

例如在导入一个py文件时,文件中包含有装饰器以及被装饰的函数,那么导入后,会发现实际上装饰器已经执行了封装的效果,引入的函数是已经装饰过的

变量作用域

  • 一般来说,函数体中的变量优先在函数体内寻找,如果找不到那么才从全局作用域寻找,
  • 但要注意+=的使用,使用该运算符的前提是变量已经定义。
  • 并且,如果在打印输出的后有定义该变量,那么执行依然会报错
1
2
3
4
5
6
7
y = 7
def f1(x):
print(x)
print(y)
y = 3

// 报错,print(y)会报错y未定义

global可以使用该参数,定义全局变量

闭包

对一个函数进行了延伸,可以使用被装饰函数外的非全局变量

1
2
3
4
5
6
7
// 计算动态平均值
def make_averager():
series = [] //必须是可变对象
def averager(new_value):
series.append(new_value) total = sum(series)
return total/len(series)
return averager
  • series是make_averager函数的局部变量,返回后该局域变量域就已经结束了
  • 对averager函数而言,series是一个自由变量(指未在本地作用域中绑定的变量)
  • 闭包是一种函数,它会保留定义函数时存在的自由变量的绑定, 这样调用函数时,虽然定义作用域不可用了,但是仍能使用那些绑定
  • 只有嵌套在其他函数中的函数才可能需要处理不在全局作用域中的外部变量

注意

  1. 自由变量必须是一个可变的对象,否则在被装饰函数体内使用该变量会报错未定义
1
2
3
4
5
6
7
8
9
10
11
12
13
def make_averager():

count = 0
total = 0

def averager(new_value):

count += 1
total += new_value
return total / count

return averager
// 报错,count未定义,因为在averager函数体内部,未count赋值了,那么会将count变成局部变量,total变量也是一样
  1. python3中有关键字nonlocal,可以保存在闭包中使用的变量

functools模块中常用装饰器

functools.wraps

该装饰器可以将被装饰的函数属性复制到装饰函数中,常用来恢复被装饰函数修改过的namedoc属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import time 
import functools

def clock(func):

@functools.wraps(func)
def clocked(*args, **kwargs):

t0 = time.time()
result = func(*args, **kwargs)
elapsed = time.time() - t0
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

functools.lu_cache

主要用来缓存,它把耗时的函数的结果保存起来,避免传入相同的参数时重复计算。LRU 三个字母是“Least Recently Used”的缩写,表明缓存不会无限制增长,一段时间不用的缓存 条目会被扔掉。

functools.singleddispatch

将一个装饰器扩展成多个来使用,可以在系统的任何地方和 任何模块中注册专门函数。如果后来在新的模块中定义了新的类型,可 以轻松地添加一个新的专门函数来处理那个类型。此外,你还可以为不 是自己编写的或者不能修改的类添加自定义函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from functools import singledispatch 
from collections import abc
import numbers
import html

@singledispatch
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)

@htmlize.register(tuple)
@htmlize.register(abc.MutableSequence)
def _(seq):
inner = '</li>\n<li>'.join(htmlize(item) for item in seq)
return '<ul>\n<li>' + inner + '</li>\n</ul>'

参数化装饰器

创建一个装饰器 工厂函数,把参数传给它,返回一个装饰器,然后再把它应用到要装饰 的函数上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
registry = set()
def register(active=True):
def decorate(func):
print('running register(active=%s)->decorate(%s)' % (active, func))
if active:
registry.add(func)
else:
registry.discard(func)
return func
return decorate

@register(active=False)
def f1():
print('running f1()')

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

def f3():
print('running f3()')