python - 直接从 typing.NamedTuple 继承时出现奇怪的 MRO 结果

标签 python python-3.x namedtuple python-typing python-mro

我很困惑为什么FooBar.__mro__不显示 <class '__main__.Parent'>像上面两个。

在深入研究了 CPython 源代码之后,我仍然不知道为什么。

from typing import NamedTuple
from collections import namedtuple

A = namedtuple('A', ['test'])

class B(NamedTuple):
  test: str

class Parent:
  pass

class Foo(Parent, A):
  pass

class Bar(Parent, B):
  pass

class FooBar(Parent, NamedTuple):
  pass

print(Foo.__mro__)
# prints (<class '__main__.Foo'>, <class '__main__.Parent'>, <class '__main__.A'>, <class 'tuple'>, <class 'object'>)

print(Bar.__mro__)
# prints (<class '__main__.Bar'>, <class '__main__.Parent'>, <class '__main__.B'>, <class 'tuple'>, <class 'object'>)

print(FooBar.__mro__)
# prints (<class '__main__.FooBar'>, <class 'tuple'>, <class 'object'>)
# expecting: (<class '__main__.FooBar'>, <class '__main__.Parent'>, <class 'tuple'>, <class 'object'>) 

最佳答案

这是因为 typing.NamedTuple不是真正的正确类型。这是一个类。但它的唯一目的是利用元类魔法为您提供一种方便的好方法来定义命名元组类型。命名元组派生自 tuple直接地。
请注意,与大多数其他类(class)不同,

from typing import NamedTuple
class Foo(NamedTuple):
    pass

print(isinstance(Foo(), NamedTuple))
打印 False .
这是因为在 NamedTupleMeta 本质上是自省(introspection) __annotations__在你的类中最终使用它来返回一个由调用 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):
        pass
    return nm_tpl

class NamedTupleMeta(type):

    def __new__(cls, typename, bases, ns):
        if ns.get('_root', False):
            return super().__new__(cls, typename, bases, ns)
        types = ns.get('__annotations__', {})
        nm_tpl = _make_nmtuple(typename, types.items())
        ...
        return nm_tpl
当然,namedtuple本质上只是创建一个派生自 tuple 的类.实际上,您的命名元组类在类定义语句中派生的任何其他类都将被忽略,因为这颠覆了通常的类机制。它可能感觉不对,在很多方面它很丑陋,但实用性胜过纯粹性。能够编写如下内容既好又实用:
class Foo(NamedTuple):
    bar: int
    baz: str

关于python - 直接从 typing.NamedTuple 继承时出现奇怪的 MRO 结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60707607/

相关文章:

python - 一起使用堆排序和快速排序

python - 当 terraform 从 python 子进程运行时,为什么 args 被 '=' 分割

python-3.x - 使用 pandas datareader 从 alpha vantage 提取股票数据时出错

python - 为什么包含可变对象的 NamedTuple 可哈希,而包含可变对象的 Tuple 不可哈希?

python - 从以前的字段向 NamedTuple 添加字段

python - 在 Python 中按所有项目的数字排序列表

python - 使用 gspread 将 CSV 上传到 Google 表格

visual-studio - Intellisense 无法识别 Python 3 的类型提示

python - 可迭代解包和切片分配

Python:将具有unicode of namedtuple的字符串形式转换为namedtuple