简体   繁体   English

python动态创建类方法

[英]python create class methods on the fly

I have a rather complex base class for some sqlalchemy models and I want to create a rails style setters but, since i'm fairly new to python, I'm stuck with a problem that I cant seem to bypass. 对于某些sqlalchemy模型,我有一个相当复杂的基类,并且我想创建一个Rails样式设置器,但是,由于我对python相当陌生,所以我遇到了一个似乎无法绕过的问题。 I create the setters on the new method so I can have the triggered both on new instances and on queries but no matter what setter I define and execute it always picks the last setter to execute. 我在方法上创建了设置器,因此可以在新实例和查询上都触发该设置器,但是无论我定义和执行什么设置器,它总是会选择要执行的最后一个设置器。 An example serves better: 一个例子效果更好:

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')

As you can see I'm setting the email but, when executed, the code tries to set the username which is the last column. 如您所见,我正在设置电子邮件,但是在执行时,代码尝试设置用户名,即最后一列。 Any idea why is that? 知道为什么吗?

This happens because your lambda function references column but doesn't pass it in as an argument: 发生这种情况是因为您的lambda函数引用了column但没有将其作为参数传递:

lambda cls, v: cls.setAttribute( cls, column, v )

When this function is executed, it will look for the name column in a containing or global scope, and always find the value 'username' because that is what column was set to last. 执行此函数时,它将在包含或全局范围内查找name column ,并始终找到值'username'因为那是设置为最后一个column的值。

Here is a straightforward way to fix this using a default argument value: 这是使用默认参数值解决此问题的简单方法:

    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 )

Another alternative would be to use a closure (in a way, the mutable default argument is a type of closure): 另一种选择是使用闭包(在某种程度上,可变的默认参数是闭包的类型):

    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 )

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM