`namedtuple()` 与 Python 中的 `NamedTuple`

问题描述 投票:0回答:2

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

from typing import NamedTuple

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

和:

from collections import namedtuple

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

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

python python-3.x difference namedtuple
2个回答
109
投票

子类化

typing.NamedTuple
生成的类型相当于
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
是一个类,它使用metaclasses和自定义
__new__
来处理注释,然后它委托给
collections.namedtuple
来构建并返回类型
。正如您可能从小写名称约定中猜到的那样,
collections.namedtuple
不是类型/类 - 它是工厂函数。 它的工作原理是构建一个 Python 源代码字符串,然后对该字符串调用
exec
生成的构造函数从命名空间中取出,并包含在元类type
的3参数调用中以构建并返回您的类。这解释了上面看到的奇怪的继承破坏,
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
元类杂技已经消失,现在它只是一个简单的工厂函数,它调用 

collections.namedtuple

,然后在返回的类型上设置 
__annotations__
。现在不允许使用 
NamedTuple
 进行多重继承(它一开始就无法正常工作)。

有关更改,请参阅

bpo40185 / GH-19371


-1
投票

namedtuple()无法设置字段的类型,而NamedTuple的字段必须具有如下所示的类型:

from collections import namedtuple from typing import NamedTuple Person = namedtuple(typename='Person', field_names=['name', 'age']) class Person(NamedTuple): name: str # Here age: int # Here p = Person(name='John', age=36) print(p, p.name, p.age) # Person(name='John', age=36) John 36
    
最新问题
© www.soinside.com 2019 - 2025. All rights reserved.