1

さて、私はブードゥーとメタクラスを使ったPythonの暗い側面をいじり始めました。さらに変更する前に、簡単な例を実行したかったのですが、このコードを実行したときにprintステートメントが出力されないのはなぜですか。

class Versioned(type):
    def __new__(cls, name, bases, dct):
        return type.__new__(cls, name, bases, dct)

    def __setattr__(cls, key, value):
        print "Setting attr %s to %s" % (key, value)
        return object.__setattr__(cls, key, value)

class Test(object):
    __metaclass__ = Versioned

    def __init__(self):
        self.a = 1
        self.b = 2

a = Test()
a.b = 31
print dir(a)
4

3 に答える 3

2

理解しておくべきことは、メタクラスはそれ自体がクラスではないということです。これは、クラスを返す呼び出し可能オブジェクト(クラス ファクトリ)です。と同じ目的を果たしますtype(__name__, __bases__, __dict__)

>>> type('myclass', (), {})
<class '__main__.myclass'>

を定義するとき__metaclass__、あなたは単にoverriding the default class factory for that specific class or moduleです。たとえば、これはメタクラスです。

def setattr_logging_class(name, bases, dict_):
    """I am a metaclass"""
    def __setattr__(self, k, v):
        print "{} set attribute {} to {}".format(self, k, v)
        super(self.__class__, self).__setattr__(k, v)
    dict_['__setattr__'] = __setattr__
    cls = type(name, bases, dict_)
    return cls

class MyClass(object):
    __metaclass__ = setattr_logging_class
    def __init__(self):
        self.a = 1

obj = MyClass()
obj.b = 2

print obj.__dict__

最も重要なことは、 (変更しない限りbases) 作成されたクラスのメソッド解決にメタクラスが関与しないことです。これが、あなたの がインスタンスVersioned.__setattr__に表示されない理由です。Testすべてが同じで新しいクラス ( typeではなく type ) をVersioned返し、Python ランタイムがブロックから解析されました。Versionedtypenamebasesdict_class Test(object):

クラス自体は (__new__メソッドを介して) 呼び出すことができます。(__new__はクラス__call__用であり、インスタンス用です。)

class MyClass(object):
    def __new__(cls_self, *args, **kwargs):
        print "I am {} called with {} and {}".format(cls_self, args, kwargs)
        return None

myobj = MyClass(1,2,3,a=4,b=5,c=6)
print myobj # == None

クラスは呼び出し可能であるため、クラスをメタクラスとして使用できます。実際、typeクラスです。ただし、メソッドでインスタンスを使用することもできます__call__! これら 2 つの例は似ており、どちらの方法でも実行できないことはありません。(実際、通常、オブジェクトを使用する方が簡単です。)

class MetaClass(type):
    def __new__(cls, name, bases, dict_):
        print "I am {} called with {}, {}, {}".format(cls, name, bases, dict_)
        return type.__new__(cls, name, bases, dict_)

class MetaObject(object):
    def __call__(self, name, bases, dict_):
        print "I am {} called with {}, {}, {}".format(self, name, bases, dict_)
        return type(name, bases, dict_)


class MyClass(object):
    __metaclass__ = MetaClass


class MyClass2(object):
    __metaclass__ = MetaObject()

Python には、メタクラス以外にも多くのメタプログラミング手法があることに注意してください。特に、Python >= 2.6 では、クラス デコレータのサポートが追加されました。これは、はるかに単純なインターフェイスでメタクラスのユース ケースのほとんどをカバーします。(自分でクラスを作成するのではなく、作成済みのクラス オブジェクトを変更して取得します。)

私が最近書いたこのスタックオーバーフローの回答で、複数のメタプログラミング方法の調査を見ることができます。元の質問は、「組み込みの Python コンテナー タイプをスレッドセーフにするにはどうすればよいですか?」というものでした。

于 2012-12-28T17:10:53.870 に答える
1

__setattr__インスタンスではなく、クラス自体に対して定義しています。これにより、print ステートメントが実行されます。

Test.key = value
于 2012-12-28T17:16:10.863 に答える
1

私は実際にメタクラスを使用したことはありませんが、継承とメタクラスを混在させていると思います。メタクラスはインスタンスを構築するためにありますが、インスタンスに暗黙的に何も追加しません。__setattr__メソッドを にインポートする理由はありませんTestTest明示的に追加するには、辞書 ( )を変更する必要がありますdct。のようなものdct["__setattr__"] = cls.__setattr__。しかし、うまくいかず、その理由がわかりません。それを機能させる唯一の方法は次のとおりです。

class Versioned(type):
    def __new__(cls, name, bases, dct):
        print("BLAH")
        def __setattr__(cls, key, value):
            print("Setting attr %s to %s" % (key, value))
            return object.__setattr__(cls, key, value)
        dct["__setattr__"] = __setattr__
        return type.__new__(cls, name, bases, dct)

これが役立つことを願っています:-)

最後に、このリンクは Python のメタクラスを理解するのに役立ちます。

于 2012-12-28T15:40:30.313 に答える