自从我第一次了解 Python 中的元类时,我一直想知道“元元类可以做什么?”。这至少是 10 年前的事了——而现在,就在几个月前,我很清楚 Python 类创建中有一种机制实际上涉及“元-元”类。因此,可以尝试想象它的一些用途。
回顾 Python 中的对象实例化:每当在 Python 中通过使用与调用普通函数相同的语法“调用”其类来实例化对象时,类的__new__
和__init__
. 在类上“协调”调用这些方法的正是类'元类'__call__
方法。通常在 Python 中编写元类时,元类的__new__
or__init__
方法是自定义的。
因此,事实证明,通过编写“元元”类,可以自定义其__call__
方法,从而控制传递哪些参数以及元类__new__
和__init__
方法,以及是否要在这些之前或之后调用其他代码。最后的结果是,metcalsses 本身通常是硬编码的,即使在非常大的项目中,也只需要几个,如果有的话。因此,可能在“元元”调用中完成的任何定制通常直接在元类本身上完成。
还有它们,Python 元类还有其他不太常见的用途——可以自定义元类中的__add__
方法,以便它们定义的类是“可添加的”,并创建一个派生类,将两个添加的类作为超类。该机制对元类也完全有效 - 因此,我们“有一些实际代码”,遵循“元元”类的示例,该类允许仅通过在类声明中添加它们来为类组成“元类” :
class MM(type):
def __add__(cls, other):
metacls = cls.__class__
return metacls(cls.__name__ + other.__name__, (cls, other), {})
class M1(type, metaclass=MM):
def __new__(metacls, name, bases, namespace):
namespace["M1"] = "here"
print("At M1 creation")
return super().__new__(metacls, name, bases, namespace)
class M2(type, metaclass=MM):
def __new__(metacls, name, bases, namespace):
namespace["M2"] = "there"
print("At M2 creation")
return super().__new__(metacls, name, bases, namespace)
我们可以看到在交互式控制台上工作:
In [22]: class Base(metaclass = M1 + M2):
...: pass
...:
At M1 creation
At M2 creation
请注意,由于 Python 中的不同元类通常难以组合,因此通过允许将用户制作的元类与库或 stdlib 的元类组合起来,这实际上很有用,而不必将这个元类显式声明为前者的父类:
In [23]: import abc
In [24]: class Combined(metaclass=M1 + abc.ABCMeta):
...: pass
...:
At M1 creation