gpt4 book ai didi

python - 为什么 `dataclasses.asdict(obj)` > 10x 比 `obj.__dict__()` 慢

转载 作者:太空宇宙 更新时间:2023-11-03 12:36:06 25 4
gpt4 key购买 nike

我正在使用 Python 3.6 和来自 ericvsmithdataclasses backport 包.

似乎调用 dataclasses.asdict(my_dataclass) 比调用 my_dataclass.__dict__ 慢 10 倍:

In [172]: @dataclass
...: class MyDataClass:
...: a: int
...: b: int
...: c: str
...:

In [173]: %%time
...: _ = [MyDataClass(1, 2, "A" * 1000).__dict__ for _ in range(1_000_000)]
...:
CPU times: user 631 ms, sys: 249 ms, total: 880 ms
Wall time: 880 ms

In [175]: %%time
...: _ = [dataclasses.asdict(MyDataClass(1, 2, "A" * 1000)) for _ in range(1_000_000)]
...:
CPU times: user 11.3 s, sys: 328 ms, total: 11.6 s
Wall time: 11.7 s

这是预期的行为吗?在什么情况下我必须使用 dataclasses.asdict(obj) 而不是 obj.__dict__


编辑:使用 __dict__.copy() 没有太大区别:

In [176]: %%time
...: _ = [MyDataClass(1, 2, "A" * 1000).__dict__.copy() for _ in range(1_000_000)]
...:
CPU times: user 922 ms, sys: 48 ms, total: 970 ms
Wall time: 970 ms

最佳答案

在大多数情况下你会使用 __dict__ 而没有 dataclasses,你可能应该继续使用 __dict__,也许有一个 copy 调用。 asdict 做了很多您可能并不真正想要的额外工作。这是它的作用。


首先,来自docs :

Each dataclass is converted to a dict of its fields, as name: value pairs. dataclasses, dicts, lists, and tuples are recursed into. For example:

@dataclass
class Point:
x: int
y: int

@dataclass
class C:
mylist: List[Point]

p = Point(10, 20)
assert asdict(p) == {'x': 10, 'y': 20}

c = C([Point(0, 0), Point(10, 4)])
assert asdict(c) == {'mylist': [{'x': 0, 'y': 0}, {'x': 10, 'y': 4}]}

因此,如果您想要递归数据类听写,请使用 asdict。如果您不想要它,那么提供它的所有开销都被浪费了。特别是,如果您使用 asdict,则将包含对象的实现更改为使用 dataclass 将更改 asdict 对外部对象的结果。

递归逻辑也没有处理循环引用。如果您使用数据类来表示图形或任何其他具有循环引用的数据结构,asdict 将会崩溃:

import dataclasses

@dataclasses.dataclass
class GraphNode:
name: str
neighbors: list['GraphNode']

x = GraphNode('x', [])
y = GraphNode('y', [])
x.neighbors.append(y)
y.neighbors.append(x)

dataclasses.asdict(x) # crash here!

此示例中的 asdict 调用遇到了 RecursionError: maximum recursion depth exceeded while calling a Python object


除此之外,asdict 构建一个 字典,而__dict__ 只是直接访问对象的属性字典。 asdict 的返回值不会受到原始对象字段重新分配的影响。此外,asdict 使用 fields,因此如果您将属性添加到与声明的字段不对应的数据类实例,asdict 将不会包含他们。

最后,文档根本没有提到它,但是 asdictcall deepcopy在不是数据类对象、字典、列表或元组的所有内容上:

else:
return copy.deepcopy(obj)

(数据类对象、字典、列表和元组通过递归逻辑,它也构建一个副本,只是应用了递归听写。)

deepcopy 本身非常昂贵,并且缺少任何 memo 处理意味着 asdict 可能会创建共享的多个副本非平凡对象图中的对象。当心:

>>> from dataclasses import dataclass, asdict
>>> @dataclass
... class Foo:
... x: object
... y: object
...
>>> a = object()
>>> b = Foo(a, a)
>>> c = asdict(b)
>>> b.x is b.y
True
>>> c['x'] is c['y']
False
>>> c['x'] is b.x
False

关于python - 为什么 `dataclasses.asdict(obj)` > 10x 比 `obj.__dict__()` 慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52229521/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com