为何两段Python动态属性定义代码返回值不一致?
为什么两段Python脚本表现不一致?
第一段脚本
class Q: def __init__(self): self._foo = 123 self._bar = 456 self._properties = { "foo": property(lambda self: getattr(self, "_foo")), "bar": property(lambda self: getattr(self, "_bar")) } def __getattr__(self, name): if name not in self._properties: raise AttributeError(f"Property '{name}' does not exist") return self._properties[name].__get__(self, self.__class__) q = Q() print(q.foo) print(q.bar)
输出:
123 456
第二段脚本
class Q: def __init__(self): self._foo = 123 self._bar = 456 self._properties = { k: property(lambda self: getattr(self, f"_{k}")) for k in ['foo', 'bar'] } def __getattr__(self, name): if name not in self._properties: raise AttributeError(f"Property '{name}' does not exist") return self._properties[name].__get__(self, self.__class__) q = Q() print(q.foo) print(q.bar)
输出:
456 456
问题根源
这是Python中lambda表达式在循环场景下的延迟绑定特性引发的问题:
- 循环内定义的lambda不会在创建时立即捕获循环变量
k的当前值,而是等到lambda被调用时,才会去查找k的当前值。 - 字典推导式执行完毕后,循环变量
k的最终值是'bar',因此两个lambda在实际调用时,都会使用这个最终值来拼接属性名,最终都获取到_bar的值,导致输出重复。
而第一段脚本里的lambda直接写死了"_foo"和"_bar"字符串,没有依赖循环变量,每个lambda都绑定了固定的目标属性,调用时自然能返回正确结果。
修复方案
要在循环中正确生成lambda,需要通过参数默认值强制捕获循环变量的当前值,修改后的字典推导式如下:
self._properties = { k: property(lambda self, k=k: getattr(self, f"_{k}")) for k in ['foo', 'bar'] }
这里给lambda新增了k=k的参数,默认值会在lambda定义时就绑定当前循环迭代的k值,避免后续调用时使用循环变量的最终值。
内容的提问来源于stack exchange,提问作者John Forbes
相关产品推荐
相关产品推荐

