在这种情况下,类型提示和自动完成的好处将在很大程度上被忽略,因此我个人建议使用定制的DotDict 方法,如下所述。
我很好奇,所以我用dataclasses.make_dataclass 方法来计时。如果您有兴趣,我还附上了我用于基准测试的完整测试代码。
更新(6/22):我为此创建了一个库并发布在 pypi - dotwiz。一探究竟。它应该和下面的方法一样快,但有一些明显的改进。
import dataclasses
from timeit import timeit
class DotDict(dict):
__getattr__ = dict.__getitem__
__delattr__ = dict.__delitem__
def __repr__(self):
fields = [f'{k}={v!r}' for k, v in self.items()]
return f'{self.__class__.__name__}({", ".join(fields)})'
def make_dot_dict(input_dict: dict) -> DotDict:
"""
Helper method to generate and return a `DotDict` (dot-access dict) from a
Python `dict` object.
"""
return DotDict(
(
k,
make_dot_dict(v) if isinstance(v, dict)
else [make_dot_dict(e) if isinstance(e, dict) else e
for e in v] if isinstance(v, list)
else v
) for k, v in input_dict.items()
)
def main():
d = {'a': 3, 'b': 1, 'c': {'aa': 33, 'bb': [{'x': 77}]}}
X = dataclasses.make_dataclass('X', d)
n = 10_000
globals().update(locals())
time_to_make_dataclass = timeit("dataclasses.make_dataclass('X', d)", number=n, globals=globals())
time_to_instantiate_dataclass = timeit("X(**d)", number=n, globals=globals())
time_to_instantiate_dot_dict = timeit("make_dot_dict(d)", number=n, globals=globals())
print(f'dataclasses.make_dataclass: {time_to_make_dataclass:.3f}')
print(f'instantiate dataclass (X): {time_to_instantiate_dataclass:.3f}')
print(f'instantiate dotdict (DotDict): {time_to_instantiate_dot_dict:.3f}')
print()
create_instance_perc = time_to_instantiate_dot_dict / time_to_instantiate_dataclass
total_time_perc = (time_to_make_dataclass + time_to_instantiate_dataclass) / time_to_instantiate_dot_dict
print(f'It is {create_instance_perc:.0f}x faster to create a dataclass instance')
print(f'It is {total_time_perc:.0f}x faster (overall) to create a DotDict instance')
# create new `DotDict` and check we can use dot-access as well as dict-access
dd = make_dot_dict(d)
assert dd.b == 1
assert dd.c.aa == 33
assert dd['c']['aa'] == 33
assert dd.c.bb[0].x == 77
# create new dataclass `X` instance
x = X(**d)
# assert result is same between both DotDict and dataclass approach
assert dd == x.__dict__
if __name__ == '__main__':
main()
我在我的 Mac(M1 芯片)上收到以下结果:
dataclasses.make_dataclass: 1.342
instantiate dataclass (X): 0.002
instantiate dotdict (DotDict): 0.013
It is 6x faster to create a dataclass instance
It is 100x faster (overall) to create a DotDict instance
正如预期的那样,我发现DotDict 方法在一般情况下的整体性能要好得多。这主要是因为它不需要动态生成一个新的类,扫描一次dict对象就可以生成dataclass字段及其类型。
虽然最初创建了类,但我惊讶地发现数据类方法在平均情况下的性能大约提高了 5 倍。