通用元类来跟踪子类?

发布于 2024-09-27 06:48:49 字数 1091 浏览 1 评论 0原文

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

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

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

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

__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__ 在读取类定义时有一个特殊的处理,如 自定义类创建

我正在寻找建议来做到这一点(在运行时更改类的元类,将其应用于其子类,或者任何其他替代方案)。

I'm trying to write a generic metaclass to track subclasses

Since I want this to be generic, I didn't want to hardcode any class name within this metaclass, therefore I came up with a function that generates the proper metaclass, something like:

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

This way I could invoke it to generate a metaclass for a specific root class with:

__metaclass__ = make_subtracker(Root)

Here is where I bump into a problem. I cannot do this:

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

...because Root is not defined yet when I use make_subtracker(Root). I tried adding the __metaclass__ attribute later, so that at least it can be applied in subclasses:

class Root(object):
   _registry = []

Root.__metaclass__ = make_subtracker(Root)

...but this doesn't work. __metaclass__ has a special processing when the class definition is read, as defined in Customizing class creation.

I'm looking for suggestions in order to do this (either change a class' metaclass at runtime in a way that it is applied to its subclasses, or any other alternative).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

森林迷了鹿 2024-10-04 06:48:49

Python 会自动为新型类执行此操作,如 回答类似的问题如何查找给定名称的类的所有子类? 此处。

Python does this automatically for new-style classes, as mentioned in this answer to the similar queston How to find all the subclasses of a class given its name? here.

顾挽 2024-10-04 06:48:49

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

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 子句中,以便该类不会将自身注册为子类。

I think you want something like this (untested):

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)

Then, for Python 2, you can invoke it like:

class Root(object):
    __metaclass__ = SubclassTracker

for Python 3

class Root(object, metaclass=SubclassTracker):

Note that you don't need to stick the _registry attribute on there because stuff like that is what metaclasses are for. Since you already happen to have one laying around... ;)

Note also that you might want to move the registration code into an else clause so that the class doesn't register itself as a subclass.

烟─花易冷 2024-10-04 06:48:49

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

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

Here's something I've been playing around with (that works):

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文