我的问题是受此question启发的。
存在3级类模型的问题-终止类(3级)仅应存储在注册表中,但是2级具有干扰性并且也已存储,因为它们是1级的子类。一级。
我想通过使用metaclass摆脱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__
无效?答案 0 :(得分:1)
我想出并使用/喜欢的解决方案是:
class Meta_Parent(type):
def _init_subclass_override(cls, **kwargs):
super().__init_subclass__(**kwargs)
# Do whatever... I raise an exception if something is wrong
#
# i.e
# if sub-class's name does not start with "Child_"
# raise NameError
#
# cls is the actual class, Child_A in this case
class Parent_One(metaclass=Meta_Parent):
@classmethod
def __init_subclass__(cls, **kwargs):
Meta_Parent._init_subclass_override(cls, **kwargs)
### Parent_One's childs
class Child_A(Parent_One):
pass
我之所以这样,是因为它使子类创建代码/检查程序干燥。同时,如果看到Parent_One
,则说明创建子类时会发生某些事情。
我是在模拟自己的接口功能(而不是使用ABC)的同时这么做的,override
方法检查子类中某些方法的存在。
可以争论重写方法是真正属于元类还是其他地方。
答案 1 :(得分:0)
__init__
有效,但__init_subclass__
无效?我通过调试 GDB 的 CPython 找到了答案。
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
函数,它依次调用 mro 链中的__new__
方法搜索。 tp_init
也会发生同样的情况。子类初始化发生在新类型创建-init_subclass(type, kwds)的结尾。它使用super object在刚创建的新对象的mro链中搜索__init_subclass__
方法。在我的情况下,对象的mro链有两个项目:
print(Parent_One.__mro__)
### Output
(<class '__main__.Parent_One'>, <class 'object'>).
int res = type->tp_init(obj, args, kwds);
是对象初始化。它还在mro链中搜索__init__
方法,,但使用元类mro,而不是刚创建的新对象的mro 。在我的情况下,元类mro具有三个项目:
print(Meta_Parent.__mro__)
###Output
(<class '__main__.Meta_Parent'>, <class 'type'>, <class 'object'>)
因此,答案是::在不同位置搜索__init_subclass__
和__init__
方法:
__init_subclass__
的{{1}}中搜索Parent_One
,然后在__dict__
的{{1}}中搜索。object
:__dict__
的{{1}},__init__
的{{1}},Meta_Parent
的{{ 1}}。我想出了以下解决方案。它有缺点-__dict__
方法由每个子类(包括子类)调用,这意味着-所有子类都具有type
和__dict__
属性,这是不必要的。但这按我在问题中的要求进行。
object
输出
__dict__