Python 3-访问数据的速度最快的是数据类还是字典?

Sér*_*fra 2 python dictionary python-3.7 python-dataclasses

Python 3.7引入了数据类来存储数据。我正在考虑采用这种新方法,该方法比命令更井井有条。

但是我有一个疑问。Python将键转换为dict上的哈希,这使得查找键和值的速度更快。数据类实现类似的东西吗?

哪一个更快?为什么?

Tre*_*oss 8

@Arne 有一个很好的答案,并证明字典确实是两者中更快的一个。让我补充几件事:

正如我在评论中提到的,从 Python 3.10 开始,有一个@dataclass(slots=True)选项可以创建带有槽成员的数据类,与 Arne 的更快的示例完全相同。没有太多理由不使用它slots=True,除非你知道你需要它。

现在谈谈另一个鲜为人知的替代方案。您可能选择数据类而不是字典的主要原因之一是为了 IDE 提示(例如智能感知)以及对预期键是否存在的健全性检查。自 python 3.8 以来,出现了PEP589 TypedDict,它确实允许使用字典的标准格式。考虑以下:

from typing import TypedDict

class Movie(TypedDict):
    name: str
    year: int

movie: Movie = {'name': 'Blade Runner',
                'year': 1982}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,您的 IDE 将能够提示您哪些键有效,并显示正确的 init 函数:

IDE截图访问 IDE 截图初始化

此外,mypy 将能够告诉您密钥访问是否有错误;或多或少,TypedDict我们可以在不使用数据类的情况下获得一些巨大的dataclass好处。总的来说,如果您已经在使用字典,或者仍然需要字典的东西,例如简单的可嵌套性和稍微更好的性能,那么这是一个很好的解决方案。*请参阅上面的 PEP 链接以获取许多很好的示例。

*性能数字微不足道 - 如果数据类让您的生活更轻松,请使用它们。不要过早地优化一些不适合的东西。太多的程序员试图缩短纳秒的时间,而不是从更宏观的角度来了解他们的代码正在做什么,这让事情变得更加困难。


Arn*_*rne 5

python中的所有类实际上都在后台使用字典来存储其属性,如您在本文中所读。有关python类(以及更多事物)如何工作的更深入的参考,您还可以查看有关python的datamodel的文章,特别是有关自定义类的部分。

因此,从字典到数据类的迁移通常不会造成性能损失。但是最好确保使用timeit模块:


基准线

# dictionary creation
$ python -m timeit "{'var': 1}"
5000000 loops, best of 5: 52.9 nsec per loop

# dictionary key access
$ python -m timeit -s "d = {'var': 1}" "d['var']"
10000000 loops, best of 5: 20.3 nsec per loop
Run Code Online (Sandbox Code Playgroud)

基本数据类

# dataclass creation
$ python -m timeit -s "from dataclasses import dataclass" -s "@dataclass" -s "class A: var: int" "A(1)" 
1000000 loops, best of 5: 288 nsec per loop

# dataclass attribute access
$ python -m timeit -s "from dataclasses import dataclass" -s "@dataclass" -s "class A: var: int" -s "a = A(1)" "a.var" 
10000000 loops, best of 5: 25.3 nsec per loop
Run Code Online (Sandbox Code Playgroud)

在这里我们可以看到使用类确实有一些开销。对于类的创建,它要慢很多(〜5倍),但是只要您不打算每秒创建和抛弃数据类多次,就不必关心它。

属性访问可能是更重要的指标,虽然数据类再次变慢(约1.25倍),但是这次并没有那么多。

如果您认为这仍然有点慢,则可以使用插槽而不是字典来存储其属性来调整数据类(或实际上是任何类):


开槽数据类

# dataclass creation
$ python -m timeit -s "from dataclasses import dataclass" -s "@dataclass" -s "class A: __slots__ = ('var',); var: int" "A(1)" 
1000000 loops, best of 5: 242 nsec per loop

# dataclass attribute access
$ python -m timeit -s "from dataclasses import dataclass" -s "@dataclass" -s "class A: __slots__ = ('var',); var: int" -s "a = A(1)" "a.var"
10000000 loops, best of 5: 21.7 nsec per loop
Run Code Online (Sandbox Code Playgroud)

通过使用这种模式,我们可以节省更多的纳秒。在这一点上,至少在属性访问方面,字典应该不再有明显的区别,并且您可以使用数据类的优点而不会影响速度。

  • Python3.10 现在附带`@dataclass(slots=True)`!这模拟了所演示的分槽数据类的功能。使用 `python -m timeit -s "from dataclasses import dataclass" -s "@dataclass(slots=True)" -s "class A: var: int" "A(1)"` 进行创建和 `python -m timeit -s "from dataclasses import dataclass" -s "@dataclass(slots=True)" -s "class A: var: int" -s "a = A(1)" "a.var"` 用于访问,计时为与我运行的分槽数据类示例相同。 (5认同)
  • 感谢您的回答!非常完整,简洁,解决了我所有的疑惑! (3认同)
  • 尝试从字典列表中创建 10 000 个数据类。这确实会花费很多时间,访问不是问题,问题是创建,那就很慢了。 (3认同)

小智 5

虽然我是数据类的忠实粉丝,并且它们通常会带来更优雅的情况,但性能差异实际上可能是巨大的。我们最近重构了一个数据处理应用程序,该应用程序使用字典来代替数据类,结果发现吞吐量下降了100 倍以上。以前需要几毫秒才能处理的有效负载,现在只需要几秒钟。

该代码没有做任何特别复杂的事情,但确实映射了数据结构之间的各种条目。对运行进行分析表明,几乎所有的执行时间都被各种内置数据类方法占用(特别是_asdict_inner(),它占用了总时间的 30% 左右),因为每当发生任何数据操作时都会执行这些方法 - 例如合并一个结构进入另一个。使用分槽数据类仅带来约 10% 的加速。我确信其他改进也是可能的,但差距是如此之大,以至于看起来不值得。

我们改回使用 TypedDicts,性能恢复到原来的水平。TypedDict 不具有数据类的所有优点(例如运行时的类型检查和强制执行),但对于性能敏感的应用程序来说,这种权衡似乎是理所当然的。

  • 我认为人们会对这个案例、分析等感兴趣。如果您在这里扩展您的帖子以进入像其他帖子一样的示例,那将是一个巨大的优势。 (7认同)