1

对于某些 sqlalchemy 模型,我有一个相当复杂的基类,我想创建一个 rails 样式的设置器,但是,由于我对 python 还很陌生,所以我遇到了一个我似乎无法绕过的问题。我在新方法上创建了 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')

如您所见,我正在设置电子邮件,但是在执行时,代码会尝试设置作为最后一列的用户名。知道为什么吗?

4

1 回答 1

2

发生这种情况是因为您的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 )
于 2012-06-10T17:22:19.250 回答