我正在使用python 3.6和ericvsmith中的dataclassesbackport包。
似乎调用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开始:
每个数据类都转换为其字段的dict,即name:value对。数据类、dict、list和元组递归到中。例如:

@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还构建了一个新的dict,而__dict__只是直接访问对象的属性dict。asdict的返回值不受重新分配原始对象字段的影响另外,asdict使用fields,因此如果向与声明字段不对应的数据类实例添加属性,asdict将不包含这些属性。
最后,文档根本没有提到它,但是对于不是dataclass对象、dict、list或tuple的所有内容,asdict都将call deepcopy
else:
    return copy.deepcopy(obj)

(数据类对象、dict、list和元组通过递归逻辑,该逻辑也会生成一个副本,只是应用了递归措辞。)
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

10-08 08:42