Sér*_*fra 2 python dictionary python-3.7 python-dataclasses
Python 3.7引入了数据类来存储数据。我正在考虑采用这种新方法,该方法比命令更井井有条。
但是我有一个疑问。Python将键转换为dict上的哈希,这使得查找键和值的速度更快。数据类实现类似的东西吗?
哪一个更快?为什么?
@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 函数:
此外,mypy 将能够告诉您密钥访问是否有错误;或多或少,TypedDict我们可以在不使用数据类的情况下获得一些巨大的dataclass好处。总的来说,如果您已经在使用字典,或者仍然需要字典的东西,例如简单的可嵌套性和稍微更好的性能,那么这是一个很好的解决方案。*请参阅上面的 PEP 链接以获取许多很好的示例。
*性能数字微不足道 - 如果数据类让您的生活更轻松,请使用它们。不要过早地优化一些不适合的东西。太多的程序员试图缩短纳秒的时间,而不是从更宏观的角度来了解他们的代码正在做什么,这让事情变得更加困难。
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)
通过使用这种模式,我们可以节省更多的纳秒。在这一点上,至少在属性访问方面,字典应该不再有明显的区别,并且您可以使用数据类的优点而不会影响速度。
小智 5
虽然我是数据类的忠实粉丝,并且它们通常会带来更优雅的情况,但性能差异实际上可能是巨大的。我们最近重构了一个数据处理应用程序,该应用程序使用字典来代替数据类,结果发现吞吐量下降了100 倍以上。以前需要几毫秒才能处理的有效负载,现在只需要几秒钟。
该代码没有做任何特别复杂的事情,但确实映射了数据结构之间的各种条目。对运行进行分析表明,几乎所有的执行时间都被各种内置数据类方法占用(特别是_asdict_inner(),它占用了总时间的 30% 左右),因为每当发生任何数据操作时都会执行这些方法 - 例如合并一个结构进入另一个。使用分槽数据类仅带来约 10% 的加速。我确信其他改进也是可能的,但差距是如此之大,以至于看起来不值得。
我们改回使用 TypedDicts,性能恢复到原来的水平。TypedDict 不具有数据类的所有优点(例如运行时的类型检查和强制执行),但对于性能敏感的应用程序来说,这种权衡似乎是理所当然的。
| 归档时间: |
|
| 查看次数: |
536 次 |
| 最近记录: |