gpt4 book ai didi

python-3.x - 元类的 "__init_subclass__"方法在这个元类构造的类中不起作用

转载 作者:行者123 更新时间:2023-12-04 01:03:24 25 4
gpt4 key购买 nike

我的问题灵感来自这个 question .

问题在于 3 级类模型 - 终止类(3 级)仅应存储在注册表中,但 2 级是干扰性的并且也已存储,因为它们是 1 级的子类。

我想通过使用元类摆脱 1 级类。通过这种方式,只剩下 2 个类级别 - 每组设置及其子项的基类 - 从相应的基类继承的各种设置类。元类用作类工厂 - 它应该创建具有所需方法的基类,并且不应显示在继承树中。

但我的想法行不通,因为似乎__init_subclass__方法(方法的链接)不会从元类复制到构造的类。对比__init__方法,按我的预期工作。

代码片段№1.模型的基本框架:

class Meta_Parent(type):
pass

class Parent_One(metaclass=Meta_Parent):
pass

class Child_A(Parent_One):
pass

class Child_B(Parent_One):
pass

class Child_C(Parent_One):
pass

print(Parent_One.__subclasses__())

输出:
[<class '__main__.Child_A'>, <class '__main__.Child_B'>, <class '__main__.Child_C'>]

我想为上述模型的子类化过程添加功能,所以我重新定义了 type的内置 __init_subclass__像这样:

代码片段 № 2。
class Meta_Parent(type):
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
print(cls)

在我看来,现在每个新类,由 构建Meta_Parent 元类(例如, Parent_One )应该有 __init_subclass__方法,因此,当每个类都从这个新类继承时,应该打印子类名称,但它什么也不打印。也就是我的 __init_subclass__继承发生时不调用方法。

如果 有效Meta_Parent 元类是直接继承的:

代码片段 № 3。
class Meta_Parent(type):
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
print(cls)

class Child_A(Meta_Parent):
pass

class Child_B(Meta_Parent):
pass

class Child_C(Meta_Parent):
pass

输出:
<class '__main__.Child_A'>
<class '__main__.Child_B'>
<class '__main__.Child_C'>

这里没什么奇怪的, __init_subclass__正是为此目的而创建的。

我当时在想,dunder 方法只属于元类,不会传递给新构造的类,但是,我尝试使用 __init__方法,它像我一开始所期望的那样工作 - 看起来像 __init__ 的链接已复制到每个元类的类。

代码片段 № 4。
class Meta_Parent(type):
def __init__(cls, name, base, dct):
super().__init__(name, base, dct)
print(cls)

输出:
<class '__main__.Parent_One'>
<class '__main__.Child_A'>
<class '__main__.Child_B'>
<class '__main__.Child_C'>

问题:
  • 为什么__init__有效,但 __init_subclass__不是吗?
  • 是否可以通过使用元类来实现我的想法?
  • 最佳答案

    1. 为什么__init__有效,但 __init_subclass__不是吗?
    我通过调试找到了答案 CPython 来自 广发 .

  • 新类(类型)的创建始于 type_call()功能。它主要做两件事:一个新类型对象的创建和这个对象的初始化。
  • obj = type->tp_new(type, args, kwds);是一个对象创建。它调用类型的 tp_new带有传递参数的插槽。默认情况下 tp_new店铺引用basic type object's tp_new slot ,但如果任何祖先类实现了 __new__方法,引用更改为 slot_tp_new调度员功能。然后是type->tp_new(type, args, kwds);电话slot_tp_new函数,它又自己调用 __new__ 的搜索 中的方法先生链。 tp_init 也会发生同样的情况.
  • 子类初始化发生在新类型创建结束时 - init_subclass(type, kwds) .它搜索 __init_subclass__ 的mro链中的方法刚刚创建的新对象 通过使用 super object .在我的情况下,对象的 mro 链有两个项目:
    print(Parent_One.__mro__)
    ### Output
    (<class '__main__.Parent_One'>, <class 'object'>).
  • int res = type->tp_init(obj, args, kwds);是一个对象初始化。它还搜索 __init__ mro 链中的方法,但使用元类 mro,而不是刚刚创建的新对象的 mro .在我的例子中,元类 mro 有三项:
    print(Meta_Parent.__mro__)
    ###Output
    (<class '__main__.Meta_Parent'>, <class 'type'>, <class 'object'>)

  • 简化执行图:
    enter image description here
    所以,答案是: __init_subclass____init__在不同的地方搜索方法:
  • __init_subclass__首先在Parent_One中搜索的 __dict__ ,然后在 object__dict__ .
  • __init__按以下顺序搜索:Meta_Parent__dict__ , type__dict__ , object__dict__ .

  • 2. 是否可以使用元类来实现我的想法?
    我想出了以下解决方案。它有缺点 - __init__方法由每个子类调用,包括子类,这意味着 - 所有子类都有 registry__init_subclass__属性,没必要。但它按我在问题中的要求工作。
    #!/usr/bin/python3

    class Meta_Parent(type):
    def __init__(cls, name, base, dct, **kwargs):
    super().__init__(name, base, dct)
    # Add the registry attribute to the each new child class.
    # It is not needed in the terminal children though.
    cls.registry = {}

    @classmethod
    def __init_subclass__(cls, setting=None, **kwargs):
    super().__init_subclass__(**kwargs)
    cls.registry[setting] = cls

    # Assign the nested classmethod to the "__init_subclass__" attribute
    # of each child class.
    # It isn't needed in the terminal children too.
    # May be there is a way to avoid adding these needless attributes
    # (registry, __init_subclass__) to there. I don't think about it yet.
    cls.__init_subclass__ = __init_subclass__

    # Create two base classes.
    # All child subclasses will be inherited from them.
    class Parent_One(metaclass=Meta_Parent):
    pass

    class Parent_Two(metaclass=Meta_Parent):
    pass

    ### Parent_One's childs
    class Child_A(Parent_One, setting='Child_A'):
    pass

    class Child_B(Parent_One, setting='Child_B'):
    pass

    class Child_C(Parent_One, setting='Child_C'):
    pass

    ### Parent_Two's childs
    class Child_E(Parent_Two, setting='Child_E'):
    pass

    class Child_D(Parent_Two, setting='Child_D'):
    pass

    # Print results.
    print("Parent_One.registry: ", Parent_One.registry)
    print("#" * 100, "\n")
    print("Parent_Two.registry: ", Parent_Two.registry)
    输出
    Parent_One.registry:  {'Child_A': <class '__main__.Child_A'>, 'Child_B': <class '__main__.Child_B'>, 'Child_C': <class '__main__.Child_C'>}
    ####################################################################################################

    Parent_Two.registry: {'Child_E': <class '__main__.Child_E'>, 'Child_D': <class '__main__.Child_D'>}

    关于python-3.x - 元类的 "__init_subclass__"方法在这个元类构造的类中不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57660864/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com