7

メタクラスで作成されたクラスのクラス属性を破棄し、それらの引数からのデータからメソッドを構築し、動的に作成されたメソッドをクラスオブジェクトに直接アタッチするメタクラスを実装しました(問題のクラスでは、次のWebフォームオブジェクトを簡単に定義できます。 Webテストフレームワークで使用)。正常に機能していますが、より複雑なタイプのメソッドを追加する必要があります。これをクリーンに保つために、呼び出し可能なクラスとして実装しました。残念ながら、インスタンスで呼び出し可能クラスを呼び出そうとすると、インスタンスメソッドではなくクラス属性として扱われ、呼び出されたときに独自のを受け取るだけselfです。なぜこれが起こるのかはわかりますが、私が思いついたものよりも誰かがより良い解決策を持っているのではないかと期待していました。問題の簡略図:

class Foo(object):
    def __init__(self, name, val):
        self.name = name
        self.val = val
        self.__name__ = name + '_foo'
        self.name = name
    # This doesn't work as I'd wish
    def __call__(self, instance):
        return self.name + str(self.val + instance.val)

def get_methods(name, foo_val):
    foo = Foo(name, foo_val)
    def bar(self):
        return name + str(self.val + 2)
    bar.__name__ = name + '_bar'
    return foo, bar

class Baz(object):
    def __init__(self, val):
        self.val = val

for method in get_methods('biff', 1):
    setattr(Baz, method.__name__, method)
baz = Baz(10)
# baz.val == 10
# baz.biff_foo() == 'biff11'
# baz.biff_bar() == 'biff12'

私は考えました:

  1. 記述子を使用しますが、これはここで必要なものよりもはるかに複雑に思えます
  2. のためにファクトリ内でクロージャを使用しますfooが、ネストされたクロージャは、ほとんどの場合、オブジェクトの醜く厄介な置き換えです、imo
  3. インスタンスを、基本的にはデコレータとしてインスタンスにFoo渡すメソッドでインスタンスをラップします。これは、実際に追加するものですが、これは不要で、基本的に(2)と同じことを行うためのより複雑な方法のようです。selfFooinstanceBaz

私が望むことを達成するためにこれらのいずれかよりも良い方法はありますか、それとも私は弾丸を噛んでクロージャーファクトリータイプのパターンを使用する必要がありますか?

4

2 に答える 2

6

これを行う1つの方法は、呼び出し可能なオブジェクトをバインドされていないメソッドとしてクラスにアタッチすることです。メソッドコンストラクター、関数だけでなく、任意の呼び出し可能オブジェクト(つまり、__call__()メソッドを持つクラスのインスタンス)で機能します。

from types import MethodType

class Foo(object):
    def __init__(self, name, val):
        self.name = name
        self.val = val
        self.__name__ = name + '_foo'
        self.name = name
    def __call__(self, instance):
        return self.name + str(self.val + instance.val)

class Baz(object):
    def __init__(self, val):
        self.val = val

Baz.biff = MethodType(Foo("biff", 42), None, Baz)

b = Baz(13)
print b.biff()
>>> biff55

Python 3では、バインドされていないインスタンスメソッド(クラスには通常の関数がアタッチされているだけ)などはありません。代わりに、Fooクラスにメソッドを指定してバインドされたインスタンスメソッドを返す記述子を作成することもできます__get__()。(実際には、そのアプローチはPython 2.xでも機能しますが、上記の方が少しパフォーマンスが向上します。)

from types import MethodType

class Foo(object):
    def __init__(self, name, val):
        self.name = name
        self.val = val
        self.__name__ = name + '_foo'
        self.name = name
    def __call__(self, instance):
        return self.name + str(self.val + instance.val)
    def __get__(self, instance, owner):
        return MethodType(self, instance) if instance else self
        # Python 2: MethodType(self, instance, owner)

class Baz(object):
    def __init__(self, val):
        self.val = val

Baz.biff = Foo("biff", 42)

b = Baz(13)
print b.biff()
>>> biff55
于 2013-01-25T17:45:04.503 に答える
1

あなたが遭遇している問題は、あなたのオブジェクトがあなたが入れようとしている Baz クラスのメソッドとしてバインドされていないことです.これは、それが通常の関数である記述子ではないためです!

これは、記述子としてアクセスされたときにメソッドになる単純な__get__メソッドをクラスに追加することで修正できます。Foo

import types

class Foo(object):
    # your other stuff here

    def __get__(self, obj, objtype=None):
        if obj is None:
            return self # unbound
        else:
            return types.MethodType(self, obj) # bound to obj
于 2013-01-25T17:49:43.947 に答える