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

Car*_*bés 11 python metaclass

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

因为我希望这是通用的,我不想这个元类中硬编码任何类的名字,所以我想出了生成正确的元类,像一些功能:

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
Run Code Online (Sandbox Code Playgroud)

这样我就可以调用它来为特定的类生成一个元类:

__metaclass__ = make_subtracker(Root)
Run Code Online (Sandbox Code Playgroud)

这是我遇到问题的地方.我不能做到这一点:

class Root(object):
   _registry = []
   __metaclass__ = make_subtracker(Root)
Run Code Online (Sandbox Code Playgroud)

...因为Root我使用时还没有定义make_subtracker(Root).我稍后尝试添加metaclass属性,以便至少可以在子类中应用它:

class Root(object):
   _registry = []

Root.__metaclass__ = make_subtracker(Root)
Run Code Online (Sandbox Code Playgroud)

......但这不起作用.读取类定义时,metaclass有一个特殊的处理,如http://docs.python.org/reference/datamodel.html#customizing-class-creation中所定义

我正在寻找建议来做到这一点(在运行时以一种应用于其子类的方式更改类'元类,或任何其他替代方法).

aar*_*ing 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)
Run Code Online (Sandbox Code Playgroud)

然后,对于Python 2,您可以像下面这样调用它:

class Root(object):
    __metaclass__ = SubclassTracker
Run Code Online (Sandbox Code Playgroud)

对于Python 3

class Root(object, metaclass=SubclassTracker):
Run Code Online (Sandbox Code Playgroud)

请注意,您不需要_registry在那里粘贴属性,因为像这样的东西是元类的用途.因为你已经碰巧有一个... ...)

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


mar*_*eau 9

Python为新式类自动执行此操作,如对类似queston的回答中所述,如何在Python中查找给定类的所有子类?这里.