使用从字典键自动生成的字段名称创建一个数据类
Create a dataclass with field names automatically generated from dict keys
我想从 dict
创建一个 dataclass
,不仅要使用 dict
的值,还要使用自动识别为字段名称的 keys
dataclass
.
输入是
d = {'a': 3, 'b': 7}
现在我想做这样的东西
import dataclasses
# Hocus pocus
X = dataclasses.dataclass_from_dict(name='X', the_dict=d)
print(X) # <class '__main__.X'>
z = X(a=3, b=99)
print(z) # X(a=3, b=99)
这里的重点是数据类及其字段是根据字典的键自动创建的。所以不需要知道dict的结构和关键字。
到目前为止我尝试了什么
我尝试了 dataclasses.make_dataclass()
,但结果 (AUTO
) 不同于以通常方式创建的数据类 (MANUAL
)。
>>> d = {'a': 3, 'b': 7}
>>> AUTO = dataclasses.make_dataclass('AUTO', [(key, type(d[key])) for key in d])
>>> @dataclass
... class MANUAL:
... a: int
... b: int
...
>>> AUTO
<class 'types.AUTO'>
>>> MANUAL
<class '__main__.MANUAL'>
将 dataclasses.make_dataclass
与具有适当键的字典一起使用。
X = dataclasses.make_dataclass('X', d)
然后您可以使用相同类型的 dict
.
实例化 X
z = X(**d)
在这种情况下,类型提示和 auto-complete 的好处将在很大程度上被忽略,因此我个人建议采用 custom-built DotDict
方法,如下所述。
我很好奇,所以我根据 dataclasses.make_dataclass
方法计时。如果您有兴趣,我还附上了我用于基准测试的完整测试代码。
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
方法在一般情况下 整体表现更好。这主要是因为它不需要动态生成一个新的class,并扫描一次dict
对象来生成数据class字段及其类型。
尽管最初创建 class 后,我惊讶地发现 dataclass 方法在一般情况下的性能大约 5x 更好。
我想从 dict
创建一个 dataclass
,不仅要使用 dict
的值,还要使用自动识别为字段名称的 keys
dataclass
.
输入是
d = {'a': 3, 'b': 7}
现在我想做这样的东西
import dataclasses
# Hocus pocus
X = dataclasses.dataclass_from_dict(name='X', the_dict=d)
print(X) # <class '__main__.X'>
z = X(a=3, b=99)
print(z) # X(a=3, b=99)
这里的重点是数据类及其字段是根据字典的键自动创建的。所以不需要知道dict的结构和关键字。
到目前为止我尝试了什么
我尝试了 dataclasses.make_dataclass()
,但结果 (AUTO
) 不同于以通常方式创建的数据类 (MANUAL
)。
>>> d = {'a': 3, 'b': 7}
>>> AUTO = dataclasses.make_dataclass('AUTO', [(key, type(d[key])) for key in d])
>>> @dataclass
... class MANUAL:
... a: int
... b: int
...
>>> AUTO
<class 'types.AUTO'>
>>> MANUAL
<class '__main__.MANUAL'>
将 dataclasses.make_dataclass
与具有适当键的字典一起使用。
X = dataclasses.make_dataclass('X', d)
然后您可以使用相同类型的 dict
.
X
z = X(**d)
在这种情况下,类型提示和 auto-complete 的好处将在很大程度上被忽略,因此我个人建议采用 custom-built DotDict
方法,如下所述。
我很好奇,所以我根据 dataclasses.make_dataclass
方法计时。如果您有兴趣,我还附上了我用于基准测试的完整测试代码。
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
方法在一般情况下 整体表现更好。这主要是因为它不需要动态生成一个新的class,并扫描一次dict
对象来生成数据class字段及其类型。
尽管最初创建 class 后,我惊讶地发现 dataclass 方法在一般情况下的性能大约 5x 更好。