1

私はいくつかのsqlalchemyモデルのかなり複雑な基本クラスを持っており、railsスタイルのセッターを作成したいのですが、Pythonはかなり新しいので、バイパスできないような問題に悩まされています。新しいメソッドでセッターを作成して、新しいインスタンスとクエリの両方でトリガーできるようにしますが、どのセッターを定義して実行しても、常に最後に実行するセッターを選択します。例の方が適しています。

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 )

もう1つの方法は、クロージャーを使用することです(ある意味で、可変のデフォルト引数はクロージャーの一種です)。

    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 に答える