首页 > 解决方案 > namedtuple 和 NamedTuple 有什么区别?

问题描述

typing模块文档说下面的两个代码片段是等效的。

from typing import NamedTuple

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

from collections import namedtuple

Employee = namedtuple('Employee', ['name', 'id'])

它们是完全相同的东西吗?如果不是,这两种实现之间有什么区别?

标签: pythonpython-3.x

解决方案


子类化生成的类型typing.NamedTuple等价于 a collections.namedtuple,但添加了__annotations__,_field_types_field_defaults属性。出于所有实际目的,生成的代码将具有相同的行为,因为 Python 中目前没有任何内容对那些与键入相关的属性起作用(尽管您的 IDE 可能会使用它们)。

作为开发人员,typing为您的命名元组使用该模块可以实现更自然的声明式接口:

  • 您可以轻松地为字段指定默认值(编辑:在 Python 3.7 中,collections.namedtuple 获得了一个新defaults关键字,因此这不再是优势
  • 您不需要重复类型名称两次(“Employee”)
  • 您可以直接自定义类型(例如添加文档字符串或某些方法)

和以前一样,您的类将是 的子类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


推荐阅读