动态为Python类添加属性时意外生成别名,如何避免?
这个问题的核心是Python闭包的延迟绑定特性——你写的lambda里引用的attr变量,并不是在循环迭代时就被“固定”下来的,而是等到lambda被调用(也就是你访问a.a/a.b)的时候,才会去查找当前attr的值。循环结束后,attr最后停留在'b',所以两个property的getter都会去取self._b,结果自然都是1。
下面给你几种可行的解决方法,都能实现动态生成正确的property:
方法1:用lambda默认参数捕获当前值
把attr作为lambda的默认参数传入,默认参数会在lambda定义时就完成求值,这样每个lambda都会持有当前迭代的attr副本,而不是共享同一个闭包变量:
class A: def __init__(self, a, b): self._a = a self._b = b for attr in ('a', 'b'): # 新增默认参数attr=attr,定义时就绑定当前的attr值 f = lambda self, attr=attr: getattr(self, '_'+attr) setattr(A, attr, property(f, None)) a = A(0,1) print(a.a) # 输出0 print(a.b) # 输出1
方法2:用functools.partial绑定参数
借助functools.partial可以提前把attr参数绑定到getter函数上,彻底避开闭包延迟绑定的坑:
from functools import partial class A: def __init__(self, a, b): self._a = a self._b = b def get_attr(self, attr): return getattr(self, '_'+attr) for attr in ('a', 'b'): # 把当前的attr绑定到get_attr的第二个参数上 f = partial(get_attr, attr=attr) setattr(A, attr, property(f, None)) a = A(0,1) print(a.a) # 输出0 print(a.b) # 输出1
方法3:用辅助函数生成getter
写一个专门的辅助函数,每次调用它时会创建一个独立的作用域,把当前的attr值牢牢捕获进去,再返回对应的getter函数:
class A: def __init__(self, a, b): self._a = a self._b = b def make_getter(attr): # 这里的attr是函数参数,每次调用make_getter都会生成新的绑定 def getter(self): return getattr(self, '_'+attr) return getter for attr in ('a', 'b'): f = make_getter(attr) setattr(A, attr, property(f, None)) a = A(0,1) print(a.a) # 输出0 print(a.b) # 输出1
这些方法的本质都是一样的:在循环迭代的过程中,为每个getter函数绑定当前的attr值,而不是让它们共享同一个闭包变量,这样就能保证每个property都能正确对应到目标的私有属性。
内容的提问来源于stack exchange,提问作者Michiel Karrenbelt
相关产品推荐
相关产品推荐

