对于一些sqlalchemy模型我有一个相当复杂的基类,我想创建一个rails风格的setter但是,因为我对python很新,我遇到了一个我似乎无法绕过的问题。我在 new 方法上创建了setter,因此我可以在新实例和查询上触发,但无论我定义和执行什么setter,它总是选择要执行的最后一个setter。一个例子更好:
class Test(object):
columns = ['email', 'username']
def __new__( cls, *args, **kwargs ):
for column in cls.columns:
setattr( cls, "set%s" % column.capitalize(), lambda cls, v: cls.setAttribute( cls, column, v ) )
return super( Test, cls ).__new__( cls, *args, **kwargs )
@staticmethod
def setAttribute(cls, attribute, value):
print "Setting attribute %s with value %s" % ( attribute, value )
setattr( cls, attribute, value )
test = Test()
test.setEmail('test@test.com')
正如您所看到的,我正在设置电子邮件,但在执行时,代码会尝试设置最后一列的用户名。知道为什么会这样吗?
答案 0 :(得分:1)
这是因为您的lambda
函数引用了column
但未将其作为参数传递:
lambda cls, v: cls.setAttribute( cls, column, v )
执行此函数时,它将在包含或全局范围内查找名称column
,并始终找到值'username'
,因为这是column
设置为持续的值
以下是使用默认参数值解决此问题的简单方法:
def __new__( cls, *args, **kwargs ):
for column in cls.columns:
setattr( cls, "set%s" % column.capitalize(), lambda cls, v, column=column: cls.setAttribute( cls, column, v ) )
return super( Test, cls ).__new__( cls, *args, **kwargs )
另一种选择是使用闭包(在某种程度上,可变默认参数是一种闭包):
def __new__( cls, *args, **kwargs ):
def make_setter(column):
return lambda cls, v: cls.setAttribute( cls, column, v )
for column in cls.columns:
setattr( cls, "set%s" % column.capitalize(), make_setter(column))
return super( Test, cls ).__new__( cls, *args, **kwargs )