Skip to content Skip to sidebar Skip to footer

Weird Mro Result When Inheriting Directly From Typing.namedtuple

I am confused why FooBar.__mro__ doesn't show like the above two. I still don't know why after some digging into the CPython source code. from typin

Solution 1:

This is because typing.NamedTuple is not really a proper type. It is a class. But its singular purpose is to take advantage of meta-class magic to give you a convenient nice way to define named-tuple types. And named-tuples derive from tuple directly.

Note, unlike most other classes,

from typing import NamedTuple
classFoo(NamedTuple):
    passprint(isinstance(Foo(), NamedTuple))

prints False.

This is because in NamedTupleMeta essentially introspects __annotations__ in your class to eventually use it to return a class created by a call to collections.namedtuple:

def_make_nmtuple(name, types):
    msg = "NamedTuple('Name', [(f0, t0), (f1, t1), ...]); each t must be a type"
    types = [(n, _type_check(t, msg)) for n, t in types]
    nm_tpl = collections.namedtuple(name, [n for n, t in types])
    # Prior to PEP 526, only _field_types attribute was assigned.# Now __annotations__ are used and _field_types is deprecated (remove in 3.9)
    nm_tpl.__annotations__ = nm_tpl._field_types = dict(types)
    try:
        nm_tpl.__module__ = sys._getframe(2).f_globals.get('__name__', '__main__')
    except (AttributeError, ValueError):
        passreturn nm_tpl

classNamedTupleMeta(type):

    def__new__(cls, typename, bases, ns):
        if ns.get('_root', False):
            returnsuper().__new__(cls, typename, bases, ns)
        types = ns.get('__annotations__', {})
        nm_tpl = _make_nmtuple(typename, types.items())
        ...
        return nm_tpl

And of course, namedtuple essentially just creates a class which derives from tuple. Effectively, any other classes your named-tuple class derives from in the class definition statement are ignored, because this subverts the usual class machinery. It might feel wrong, in a lot of ways it is ugly, but practicality beats purity. And it is nice and practical to be able to write things like:

classFoo(NamedTuple):
    bar: int
    baz: str

Solution 2:

typing.NamedTuple isn't designed to behave like a normal base class. Look at how even NamedTuple itself isn't in the new class's MRO when you "inherit" from NamedTuple. It's really only designed to be an interface to the collections.namedtuple class factory that mypy can inspect.

When you inherit from NamedTuple, its metaclass completely ignores any base classes and creates the new class by delegating to collections.namedtuple, then fills in methods and properties and stuff from the original namespace. The new class will always inherit directly from tuple.

Post a Comment for "Weird Mro Result When Inheriting Directly From Typing.namedtuple"