繁体   English   中英

用于跟踪子类的通用元类?

[英]Generic metaclass to keep track of subclasses?

我正在尝试编写一个通用元类来跟踪子类

由于我希望这是通用的,我不想在这个元类中硬编码任何类名,因此我想出了一个生成正确元类的函数,例如:

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)

...因为当我使用make_subtracker(Root)Root尚未定义。 我稍后尝试添加 __metaclass__ 属性,以便至少它可以应用于子类:

class Root(object):
   _registry = []

Root.__metaclass__ = make_subtracker(Root)

...但这不起作用。 __metaclass__ 在读取类定义时有一个特殊的处理,如自定义类创建中所定义

我正在寻找建议以执行此操作(在运行时更改类的元类以将其应用于其子类或任何其他替代方法)。

我想你想要这样的东西(未经测试):

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

对于 Python 3

class Root(object, metaclass=SubclassTracker):

请注意,您不需要将_registry属性粘贴在那里,因为元类的用途就是这样。 因为你已经碰巧有一个躺在周围......;)

另请注意,您可能希望将注册代码移动到else子句中,以便该类不会将自己注册为子类。

Python 会自动为新样式的类执行此操作,如对类似问题的回答中所述, 如何找到给定名称的类的所有子类? 这里。

这是我一直在玩的东西(有效):

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

暂无
暂无

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

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