我正在尝试为类动态创建一些方法,但是当我创建此类的对象并访问属性时,我总是得到最后一个值。
properties = ['a', 'b', 'c']
for p in properties:
f = property(lambda self: p)
print p # you see a, b, c
print f # you can see it is 3 different objects
setattr(MyClass, p, f)
obj = MyClass()
obj.a # returns c !!! why?
obj.b # returns c !!! why?
obj.c # returns c
更新
我能够使用创建方法的函数使其工作:
def create_function(p):
def f(self,):
return p
return f
这与使用 property
无关。它在官方常见问题解答中解释为 Why do lambdas defined in a loop with different values all return the same result? .
当你这样做时:
properties = ['a', 'b', 'c']
for p in properties:
f = property(lambda self: p)
print p # you see a, b, c
print f # you can see it is 3 different objects
setattr(MyClass, p, f)
…每个f
都是p
变量的闭包。*这意味着,当你调用那个f
时,它会返回p
在其定义范围内的当前值,而不是构造闭包时 p
的值。在循环结束时,p
的值为 'c'
,因此这是您所有属性的返回值。
您可以通过(在其他解决方案中**)使用“默认值 hack”来解决此问题:
properties = ['a', 'b', 'c']
for p in properties:
f = property(lambda self, p=p: p)
print p # you see a, b, c
print f # you can see it is 3 different objects
setattr(MyClass, p, f)
现在,每个属性都有一个名为p
的参数,其默认值为函数定义时p
的值。调用时(不提供 p
参数),主体以具有该值的局部变量 p
结束,而不是闭包变量 p
指的是非本地范围。
* 实际上,它在技术上不是闭包,因为定义范围是全局的。但它的行为就像是在另一个范围内完成的一样。
** 还有一个 JavaScript 习惯用法,即创建一个新范围以在其中定义函数,(lambda p: lambda self: p)(p)
,或 functools.partial
,或者创建一个可调用对象而不是一个函数,或者……但是,尽管名称中有“hack”这个词,“默认值 hack”是一个众所周知的 Python 习语,在文档中供奉并在stdlib,它通常是最佳答案,除非您有一些其他原因需要另一个范围或部分
或自定义可调用。