Python类装饰器循环生成类方法时所有方法复用最后一次迭代值疑问
Python循环内定义函数的变量绑定问题与字符串深拷贝优化
字符串深拷贝返回同一对象的原因
Python中字符串属于不可变对象,copy模块的deepcopy方法对不可变类型做了性能优化:因为不可变对象的值永远不会被修改,不需要真正生成新的副本,直接返回原对象的引用即可,所以测试得到_1 is _2返回True是符合设计的正常表现,除了字符串,数值、元组等不可变类型都有类似的优化。
循环生成的方法全部使用最后一次迭代值的原因
这个是Python中非常经典的闭包后期绑定问题,底层机制如下:
- Python中嵌套函数(也就是你代码里的
appender)引用外层作用域的变量时,变量的取值是在函数被调用时才会去外层作用域查找,而不是在函数被定义时就固定下来。 - 你在for循环中迭代生成
appender,每次迭代都会更新ch和ch_type这两个外层变量的值,等整个循环结束后,这两个变量的值已经变成了最后一次迭代的结果,也就是text和str。 - 后续你调用任何一个生成的
append_xxx方法时,函数内部的ch和ch_type都会去外层查找此时已经变成最后一次迭代结果的变量值,所以所有方法的表现都和最后一个属性的逻辑一致。
为什么用默认参数可以解决问题
Python的函数默认参数是在函数定义阶段就计算并绑定到函数对象上的,你把ch和ch_type作为默认参数写入函数签名:
def appender(self, value, ch=ch, ch_type=ch_type):
每次循环定义appender函数时,都会把当前迭代的ch和ch_type的值作为默认值绑定到新生成的函数对象上,后续调用函数时如果不主动传这两个参数,就会直接使用定义时绑定的默认值,不会再去外层作用域查找循环变量,自然就解决了变量被覆盖的问题。
其他可选的解决方案
除了默认参数,你也可以用functools.partial提前绑定参数,效果完全一致:
from functools import partial def appender(self, value, ch, ch_type): # 原有逻辑不变 if not isinstance(value, ch_type): raise TypeError(f'`{ch}` only accepts values of `{ch_type}` type, but found {type(value)}') data: dict = getattr(self, 'data') data_list = data.setdefault(ch, []) data_list.append(value) # 循环内绑定当前的ch和ch_type bound_appender = partial(appender, ch=ch, ch_type=ch_type) setattr(kls, 'append_' + ch, bound_appender)
内容的提问来源于stack exchange,提问作者deponovo
相关产品推荐
相关产品推荐

