在多年的经验之后回到这个问题:以下是其他几个答案都没有遇到的其他原因*。
每个类只允许 1 个元类
一个类只能有 1 个元类。元类充当创建类的工厂,不可能随意将工厂混合在一起。您必须创建一个知道如何以正确顺序调用多个工厂的“组合工厂”,或者创建一个知道“父工厂”并正确使用它的“子工厂”。
如果namedtuple 使用它自己的元类,涉及任何其他元类的继承都会中断:
>>> class M1(type): ...
...
>>> class M2(type): ...
...
>>> class C1(metaclass=M1): ...
...
>>> class C2(metaclass=M2): ...
...
>>> class C(C1, C2): ...
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
相反,如果您想拥有自己的元类并从 namedtuple 类继承,则必须使用某种所谓的 namedtuple_meta 元类来做到这一点:
from namedtuple import namedtuple_meta # pretending this exists
class MyMeta(type): ...
class MyMetaWithNT(namedtuple_meta, MyMeta): ...
class C(metaclass=MyMetaWithNT): ...
..或者直接从namedtuple_meta继承自定义元类:
class MyMeta(namedtuple_meta): ...
class C(metaclass=MyMeta): ...
一开始这看起来很容易,但是编写自己的元类来与一些(复杂的)nt 元类很好地配合可能很快就会出现问题。这种限制可能不会经常出现,但经常会阻碍namedtuple 的使用。因此,让所有namedtuple 类都为type 类型绝对是一个优势,并消除了自定义元类的复杂性。
元类,还是元编程?
“为什么不使用元类?!?”这个问题忽略了一个基本问题。是:nt的目的是什么?
目的不仅仅是创建一个类工厂。如果是这样,元类将是完美的。 namedtuple 的真正目的不仅仅是最终功能,而是自动生成一个类结构,其代码简单易懂,就好像它是由经验丰富的专业人员手工编写的一样。这需要元编程——不是自动生成类,而是代码。这是两个不同的东西。它与较新的 dataclasses 模块非常相似,后者为您编写方法(而不是编写整个类,如 namedtuple)。
* Raymond Hettinger 的 comment 确实暗示了这一点:
命名元组的一个关键特性是它们完全等同于手写类。