Coming back to this question after many years of experience: below are a couple of other reasons that none of the other answers hit upon*.
Metaclass, or metaprogramming?
A fundamental question that is passed over by the question "why not just use a metaclass?!?" is: what is the purpose of nt?
The purpose is not merely to create a class factory. If it were that, the metaclass would be nearly perfect. The real purpose of namedtuple
is not just the end functionality, but automatically producing classes that are simple and easy to understand in every way, as if written by hand by an experienced professional. And this requires meta programming-- automatic generation not of a class, but of code. These are two different things. It is very similar to the newer dataclasses
module, which writes methods for you (rather than writing an entire class, like namedtuple
).
Only 1 metaclass per class allowed
A class can only have 1 metaclass. The metaclass acts as the factory that creates the class, and it isn't possible to mix factories together willy nilly. You must create either a "combinatory factory" that knows how to call the multiple factories in the correct order, or a "child factory" that knows about the "parent factory", and uses it correctly.
If the namedtuple
used its own metaclass, inheritance involving any other metaclass would break:
>>> 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
Instead, if you wanted to have your own metaclass and inherit from a namedtuple
class, you'd have to use some sort of so-called namedtuple_meta
metaclass to do that:
from namedtuple import namedtuple_meta # pretending this exists
class MyMeta(type): ...
class MyMetaWithNT(namedtuple_meta, MyMeta): ...
class C(metaclass=MyMetaWithNT): ...
..or just inherit the custom metaclass from namedtuple_meta
directly:
class MyMeta(namedtuple_meta): ...
class C(metaclass=MyMeta): ...
This looks easy at first, but writing your own metaclass that plays nicely with some (complicated) nt metaclass could become problematic very quickly. This limitation would probably not come up all THAT often, but often enough that it would hinder the usage of namedtuple
. So it is definitely an advantage to have all namedtuple
classes be of the type
type, and removing the complexity of a custom metaclass.
* Raymond Hettinger's comment does hint at it:
It is a key feature for named tuples that they are exactly equivalent to a hand-written class.