You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 13:45:07