python 装饰器,生成器
装饰器
本质上也是一个函数,可以让被装饰的函数在原代码不变的情况下添加新功能。
优点:复用性强,当多个函数需要相同的额外功能时,直接将该功能封装在一个装饰函数中,需要该额外功能的其他函数直接用装饰器修饰即可。
模板:
def decorator(func):
def wrapper(*arg, **kwarg):
...
return wrapper
生成器
最大特点是:不一次性生成所有值,而是按需生成值。
优点:提高内存效率,适合处理大型数据
a = [x * x for x in range(10000)]
b = (x * x for x in range(10000))
上述代码中,\(a\) 是一个普通的列表,而 \(b\) 是一个生成器。显然对于 \(a\),\(0 \backsim 9999\) 的平方都会被创建;而 \(b\) 中的对象会在循环枚举(不能按下标取值)时动态生成,枚举到哪个就生成哪个。
创建生成器的两种方式:
- 圆括号
- \(yield\)
对于一个函数:
def f(*arg):
...
- \(f\):表示函数本身
- \(f(*arg)\):表示一个生成器
与生成器相关的一些函数:
- \(yield\):暂停执行,并返回后面的值。
- \(next\):从刚才暂停的地方继续执行,直到遇到下一个 \(yield\)。
- \(send\):为生成器发送一个值
在函数体内,\(return\) 和 \(yield\) 的区别:
- \(yield\):保存函数内的局部变量,保持当前的执行状态,暂停函数执行,并产出一个值给调用者。
- \(return\):立即终止函数的执行,并返回一个值给调用者。
以竞赛头模块的 \(dfs\) 部分为例,深入理解装饰器和生成器:
from types import GeneratorType
def bootstrap(f, stk=[]):
def wrappedfunc(*args, **kwargs):
if stk:
return f(*args, **kwargs)
else:
to = f(*args, **kwargs)
while True:
if type(to) is GeneratorType:
stk.append(to)
to = next(to)
else:
if stk:
stk.pop()
if not stk:
break
to = stk[-1].send(to)
return to
return wrappedfunc
调用装饰器的部分:
@bootstrap
def dfs(*arg):
...
yield dfs(...)
yield x
\('@...'\) 就是一个语法糖,实际上完全等价于:
dfs = bootstrap(dfs)
其中 \(bootstrap\) 是一个包装函数的函数,在本模块中作用是将 \(dfs\) 的递归调用转化成栈模拟的过程,以加大递归深度,避免爆栈。
关于装饰器 '@bookstrap' 的使用:
需要在所有 \(return\) 的地方加上 \(yield\),使得函数不再像普通函数一样调用,而是产生一个生成器并将其压栈,并继续后续处理。特殊地,若\(dfs\) 函数是 \(None\) 类型,还需要注意在函数结尾添加一个 \(yield\)。
eg:
@bootstrap
def dfs(u:int, father:int):
dep[u] = dep[father] + 1
for v in G[u]:
if v == father: continue
yield dfs(v, u) # 每个dfs递归调用前面都需要补充一个 yield
yield # 注意这里,隐式return也需要补充yield

浙公网安备 33010602011771号