自动化创建Python属性遇异常:循环中prop返回值始终为最后一项
解决Python闭包延迟绑定导致的属性值错误问题
这是个非常经典的Python闭包陷阱,我来帮你把问题拆解明白,再给出几种靠谱的解决方案。
问题原因分析
你遇到的核心问题是Python闭包的延迟绑定特性:你在循环里定义的fget函数并没有在定义时就“固定”住当时的prop值,而是持有了对循环变量prop的引用。当循环结束后,prop最终指向的是列表里的最后一个元素'bye',所以不管你调用哪个属性的fget,都会取到这个最终值。
而doc=prop能正常工作的原因是:这个参数是立即求值的,循环每一次迭代时,都会把当时prop的当前值直接传给property的doc参数,所以每个属性的文档字符串都是对的。
解决方案
方法1:用函数默认参数捕获当前值
利用Python函数默认参数在定义时求值的特性,把当前的prop值绑定到fget的默认参数上,这样每个fget都会持有自己的prop副本:
def makecls(): cls = type('mycls', (object,), {}) for prop in ['hi', 'bye']: def fget(self, prop=prop): # 默认参数捕获当前prop值 return prop setattr(cls, prop, property(fget, doc=prop)) return cls
方法2:用functools.partial绑定参数
借助functools.partial把prop值提前绑定到fget函数上,避免闭包引用循环变量:
from functools import partial def makecls(): cls = type('mycls', (object,), {}) def fget(self, prop): return prop for prop in ['hi', 'bye']: bound_fget = partial(fget, prop=prop) setattr(cls, prop, property(bound_fget, doc=prop)) return cls
方法3:用内部工厂函数创建闭包
写一个专门的内部函数来生成fget,每次调用这个工厂函数时传入当前的prop,让内部的fget捕获这个局部变量(局部变量在每次调用时都是独立的):
def makecls(): cls = type('mycls', (object,), {}) def create_fget(current_prop): def fget(self): return current_prop return fget for prop in ['hi', 'bye']: fget = create_fget(prop) setattr(cls, prop, property(fget, doc=prop)) return cls
测试验证
不管用哪种方法,你都可以这样验证效果:
c = makecls() obj = c() print(obj.hi) # 输出'hi' print(obj.bye) # 输出'bye'
内容的提问来源于stack exchange,提问作者user2926049
相关产品推荐
相关产品推荐

