python - 直接从 typing.NamedTuple 继承时出现奇怪的 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
。
请注意,与大多数其他课程不同,
from typing import NamedTuple
class Foo(NamedTuple):
pass
print(isinstance(Foo(), NamedTuple))
打印False
。
这是因为NamedTupleMeta
本质上是在您的类中进行自省__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 - 在 Django 中向模型条目添加步骤
- c# - WPF RichTextBox:查找当前在 RichTextBox 中可见的第一个块/段落
- windows - 在 Windows 中使用 shell 脚本附加 PATH 变量中的值
- python - 创建一个 Django 模型类,其字段值之一由其他模型的字段值计算,并且它必须存在于我的实际数据库表中
- python - 使用 Django rest 框架从 2 个不同的表中过滤
- r - 为分组箱线图排序数据集
- cypress - 在特定区域移动鼠标几次
- python - 有没有办法确保 Sqlite3 不会将其他整数 0 和 1 提交到布尔列
- python - 寻找最长簇的算法。(理想情况下是 Python,可能是 Matlab)
- jitsi - Jitsi Meet - 您已断开连接