11

サブクラスを追跡する汎用メタクラスを作成しようとしています

これを汎用にしたいので、このメタクラス内にクラス名をハードコーディングしたくなかったので、次のような適切なメタクラスを生成する関数を考え出しました。

def make_subtracker(root):
    class SubclassTracker(type):
        def __init__(cls, name, bases, dct):
            print('registering %s' % (name,))
            root._registry.append(cls)
            super(SubclassTracker, cls).__init__(name, bases, dct)
    return SubclassTracker

このようにして、特定のルートクラスのメタクラスを生成するために呼び出すことができます。

__metaclass__ = make_subtracker(Root)

ここで私は問題にぶつかります。これはできない:

class Root(object):
   _registry = []
   __metaclass__ = make_subtracker(Root)

...Root使用するときはまだ定義されていないためmake_subtracker(Root)。少なくともサブクラスに適用できるように、後で __metaclass__ 属性を追加してみました。

class Root(object):
   _registry = []

Root.__metaclass__ = make_subtracker(Root)

...しかし、これは機能しません。__metaclass__ には、クラス作成のカスタマイズで定義されているように、クラス定義が読み込まれるときに特別な処理があります。

これを行うための提案を探しています(実行時にクラスのメタクラスを変更して、サブクラスに適用するか、その他の代替手段を使用します)。

4

3 に答える 3

11

同様の質問へのこの回答で述べられているように、Python は新しいスタイルのクラスに対してこれを自動的に行います。ここ。

于 2010-10-12T19:44:06.460 に答える
10

私はあなたがこのようなものが欲しいと思います(テストされていません):

class SubclassTracker(type):
    def __init__(cls, name, bases, dct):
        if not hasattr(cls, '_registry'):
            cls._registry = []
        print('registering %s' % (name,))
        cls._registry.append(cls)
        super(SubclassTracker, cls).__init__(name, bases, dct)

次に、Python 2の場合、次のように呼び出すことができます。

class Root(object):
    __metaclass__ = SubclassTracker

Python3の場合

class Root(object, metaclass=SubclassTracker):

_registryそのようなものがメタクラスの目的であるため、そこに属性を貼り付ける必要はないことに注意してください。あなたはすでに1つを置いているので...;)

elseまた、クラスがそれ自体をサブクラスとして登録しないように、登録コードを句に移動することもできます。

于 2010-10-12T13:53:47.497 に答える
2

これが私が遊んでいたものです(動作します):

def sublass_registry():
    ''' Create a metaclass to register subclasses '''

    class SublassRegistryMeta(type):
        def __init__(cls, name, bases, classdict):
            if classdict.get('__metaclass__') is SublassRegistryMeta:
                SublassRegistryMeta.lineage = [cls] # put root class at head of a list
            else:
                # sublclasses won't have __metaclass__ explicitly set to this class
                # we know they're subclassees because this ctor is being called for them
                SublassRegistryMeta.lineage.append(cls) # add subclass to list
            type.__init__(cls, name, bases, classdict)

    return SublassRegistryMeta

def subclasses(cls):
    ''' Return a list containing base and subclasses '''

    try:
        if cls.__metaclass__.lineage[0] is cls: # only valid for a root class
            return cls.__metaclass__.lineage
    except AttributeError:
        pass
    return None

class Car(object): # root class
    __metaclass__ = sublass_registry()

class Audi(Car): # inherits __metaclass__
    pass

class Ford(Car): # inherits __metaclass__
    pass

class Audi2(Audi): # sub-subclass also inherits __metaclass__
    pass

print subclasses(Car)
# [<class '__main__.Car'>, <class '__main__.Audi'>, <class '__main__.Ford'>, <class '__main__.Audi2'>]
print subclasses(Audi)
# None
于 2010-10-12T19:34:11.570 に答える