通过子类化typing.NamedTuple 生成的类型等价于collections.namedtuple,但添加了__annotations__、_field_types 和_field_defaults 属性。出于所有实际目的,生成的代码的行为都是相同的,因为 Python 中目前没有任何东西作用于那些与键入相关的属性(不过,您的 IDE 可能会使用它们)。
作为开发人员,为您的命名元组使用typing 模块可以实现更自然的声明式接口:
- 您可以轻松地为字段指定默认值(编辑:在 Python 3.7 中,
collections.namedtuplegot a new defaults keyword 所以这不再是优势)
- 您无需重复两次类型名称(“Employee”)
- 您可以直接自定义类型(例如添加文档字符串或某些方法)
和以前一样,您的类将是tuple 的子类,而实例将像往常一样是tuple 的实例。有趣的是,你的类不会是NamedTuple 的子类。如果您想知道原因,请继续阅读以了解有关实施细节的更多信息。
from typing import NamedTuple
class Employee(NamedTuple):
name: str
id: int
Python 中的行为
>>> issubclass(Employee, NamedTuple)
False
>>> isinstance(Employee(name='guido', id=1), NamedTuple)
False
typing.NamedTuple 是一个类,它使用metaclasses 和一个自定义的__new__ 来处理注解,然后是delegates to collections.namedtuple, anyway, to build and return the type。正如您可能从小写的名称约定中猜到的那样,collections.namedtuple 不是类型/类 - 它是一个工厂函数。它的工作原理是构建一串 Python 源代码,然后在该字符串上调用 exec。 generated constructor is plucked out of a namespace 和 included in a 3-argument invocation of the metaclass type 用于构建和返回您的课程。这解释了上面看到的奇怪的继承中断,NamedTuple 使用元类来使用不同的元类来实例化类对象。
Python 中的行为 >= 3.9
typing.NamedTuple 从 class 更改为 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 了解更改。