簡體   English   中英

為什么 `dataclasses.asdict(obj)` > 10x 比 `obj.__dict__()` 慢

[英]Why is `dataclasses.asdict(obj)` > 10x slower than `obj.__dict__()`

我正在使用 Python 3.6 和來自dataclasses的數據類 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做了很多你可能並不真正想要的額外工作。 這就是它的作用。


首先,來自文檔

每個數據類都轉換為其字段的字典,如名稱:值對。 數據類、字典、列表和元組被遞歸到。 例如:

 @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將不會包含它們。

最后,文檔根本沒有提到它,但asdict將對不是數據類對象、字典、列表或元組的所有內容調用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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM