我正在尝试动态地为类创建一些方法但是当我创建这个类的对象并访问属性时,我总是得到最后一个值。
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
答案 0 :(得分:4)
这与使用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'
的值为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)
,这就是所有属性返回的内容。
你可以使用“默认值hack”来解决这个问题(以及其他解决方案**):
p
现在,每个属性都有一个名为p
的参数,其默认值是函数定义时的p
值。当调用(不提供p
参数)时,正文最终会得到一个带有该值的局部变量p
,而不是一个引用非局部范围的闭包变量(lambda p: lambda self: p)(p)
。 / p>
*实际上,它在技术上并不是一个闭包,因为定义的范围是全局的。但它的行为与在另一个范围内完成的行为相同。
**还有一个JavaScript惯用法,即创建一个新的作用域来定义functools.partial
或partial
中的函数,或创建一个可调用的对象而不是一个函数,或者......但是,尽管有名称中的“hack”一词,“默认值hack”是一个众所周知的Python习惯用法,载于文档中并在stdlib中使用,它通常是最好的答案,除非你有一些其他另一个范围或{{1}}或自定义可调用的原因。