PEP-557将数据类引入Python标准库,基本上可以填充与collections.namedtuple
和typing.NamedTuple
相同的角色。现在我想知道如何分离使用namedtuple仍然是更好的解决方案的用例。
当然,如果我们需要,所有的功劳都归功于dataclass
:
property
装饰器,可管理的属性数据类优势在同一个PEP中简要解释:Why not just use namedtuple。
但是对于namedtuples这个相反的问题呢:为什么不使用dataclass呢?我想从性能的角度来看,名字元组可能更好,但尚未确认。
让我们考虑以下情况:
我们将页面维度存储在一个小容器中,该容器具有静态定义的字段,类型提示和命名访问。不需要进一步散列,比较等。
NamedTuple方法:
from typing import NamedTuple
PageDimensions = NamedTuple("PageDimensions", [('width', int), ('height', int)])
DataClass方法:
from dataclasses import dataclass
@dataclass
class PageDimensions:
width: int
height: int
哪种解决方案更可取,为什么?
附:这个问题不是that one的重复,因为在这里我问的是关于namedtuple更好的情况,而不是差异(我在询问之前检查了文档和来源)
这取决于您的需求。他们每个人都有自己的好处。
这是PyCon 2018 Raymond Hettinger - Dataclasses: The code generator to end all code generators上Dataclasses的一个很好的解释
在Dataclass中,所有实现都是用Python编写的,就像在Namedtuple中一样,所有这些行为都是免费的,因为Namedtuple是从元组继承而来的。而元组结构是用C语言编写的,这就是为什么stadard方法在Namedtuple中更快(哈希,比较等)。
但Dataclass基于dup为基于元组的Namedtuple。据此,您有使用这些结构的优点和缺点。例如,NamedTuple中的空间使用量较小,但Dataclass中的时间访问速度更快。
请看我的实验:
In [33]: a = PageDimensionsDC(width=10, height=10)
In [34]: sys.getsizeof(a) + sys.getsizeof(vars(a))
Out[34]: 168
In [35]: %timeit a.width
43.2 ns ± 1.05 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
In [36]: a = PageDimensionsNT(width=10, height=10)
In [37]: sys.getsizeof(a)
Out[37]: 64
In [38]: %timeit a.width
63.6 ns ± 1.33 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
但随着NamedTuple属性数量的增加,访问时间保持不变,因为对于每个属性,它创建一个具有属性名称的属性。例如,对于我们的情况,新类的命名空间部分将如下所示:
from operator import itemgetter
class_namespace = {
...
'width': property(itemgetter(0, doc="Alias for field number 0")),
'height': property(itemgetter(0, doc="Alias for field number 1"))**
}
在哪些情况下,namedtuple仍然是一个更好的选择?
当您的数据结构需要/可以是不可变的,可散列的,可迭代的,可拆包的,可比的时候,您可以使用NamedTuple。如果您需要更复杂的东西,例如,为数据结构继承的可能性,那么使用Dataclass。
在编程中,任何可以不可变的东西都应该是不可变的。我们获得了两件事:
这就是为什么,如果数据是不可变的,你应该使用一个命名的元组而不是数据类
我在评论中写了它,但我会在这里提到它:你肯定是有重叠的,尤其是数据类中的frozen=True
- 但仍然有一些功能,例如解包属于namedtuples,它总是不可变的 - 我怀疑他们会删除这样的命名元组