我正在使用Python 3.6和ericvsmith的dataclasses
反向软件包。
似乎呼叫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
答案 0 :(得分:6)
在大多数情况下,如果您使用__dict__
而不使用dataclasses
,则可能应该继续使用__dict__
,也许要进行copy
调用。 asdict
完成了许多您可能不需要的额外工作 。这就是它的作用。
首先,从docs:
每个数据类都将转换为其字段的字典,即名称:值对。 数据类,字典,列表和元组被递归到其中。例如:
@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
构建一个 new 字典,而__dict__
则直接访问该对象的属性dict。 asdict
的返回值不受原始对象字段重新分配的影响。另外,asdict
使用fields
,因此,如果您向数据类实例添加与声明的字段不对应的属性,则asdict
将不包含它们。
最后,文档根本没有提及它,但是asdict
将call 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