namedtuple 和 NamedTuple 有什么区别?

该typing模块的文件说,下面的两段代码是等效的。

from typing import NamedTuple

class Employee(NamedTuple):
name: str
id: int


from collections import namedtuple

Employee = namedtuple('Employee', ['name', 'id'])
它们是完全相同的东西,或者如果不是,两种实现之间有什么区别?

已邀请:
子类化生成的类型typing.NamedTuple等效于 a collections.namedtuple,但添加了__annotations__,_field_types和_field_defaults属性。出于所有实际目的,生成的代码的行为将相同,因为 Python 中目前没有任何内容作用于那些与键入相关的属性(不过,您的 IDE 可能会使用它们)。

作为开发人员,将typing模块用于命名元组允许更自然的声明式接口:

您可以轻松地为字段指定默认值(编辑:在 Python 3.7 中,collections.namedtuple 获得了一个新defaults关键字,因此这不再是优势)
您不需要重复两次类型名称(“员工”)
您可以直接自定义类型(例如添加文档字符串或某些方法)
和以前一样,您的类将是 的子类tuple,并且实例将tuple像往常一样是 的实例。有趣的是,您的类不会是NamedTuple. 如果您想知道原因,请继续阅读以获取有关实现细节的更多信息。

from typing import NamedTuple

class Employee(NamedTuple):
name: str
id: int
Python 中的行为 <= 3.8
>>> issubclass(Employee, NamedTuple)
False
>>> isinstance(Employee(name='guido', id=1), NamedTuple)
False
typing.NamedTuple是一个类,它使用元类和自定义__new__来处理注释,然后它委托给collections.namedtuple,无论如何,来构建和返回类型。正如您可能从小写的名称约定中猜到的那样,collections.namedtuple它不是类型/类——它是一个工厂函数。它的工作原理是构建一串 Python 源代码,然后调用exec该字符串。将生成的构造函数抠出一个命名空间和包含在元类的3个参数的调用type来构建并返回类。这解释了上面看到的奇怪的继承破坏,NamedTuple使用元类以便使用不同的元类来实例化类对象。

Python 中的行为 >= 3.9
typing.NamedTuple由 aclass变为 a def。

>>> issubclass(Employee, NamedTuple)
TypeError: issubclass() arg 2 must be a class or tuple of classes
>>> isinstance(Employee(name="guido", id=1), NamedTuple)
TypeError: isinstance() arg 2 must be a type or tuple of types
NamedTuple现在不允许使用多重继承(首先它不能正常工作)。

有关更改,请参阅bpo40185 / GH-19371。

要回复问题请先登录注册