我正在使用dacite将Python字典转换为dataclass。是否有一种方法可以动态地将字段添加到数据集中?与下面的示例一样,dataclass“参数”只定义了一个timeseries "timeseriesA",但可能还有其他的(通过字典提供)不能声明的。
from dataclasses import asdict, dataclass
from typing import Dict, List, Optional
from dacite import from_dict
@dataclass(frozen = True)
class TimeSeries:
name: str
unit: str
data: Optional[List[float]]
@dataclass(frozen = True)
class Parameters:
timeseriesA: TimeSeries
@dataclass(frozen = True)
class Data:
parameters: Parameters
@classmethod
def fromDict(cls, data: Dict) -> 'Data':
return from_dict(cls, data)
@classmethod
def toDict(cls) -> Dict:
return asdict(cls)
def main() -> None:
d: Dict = {
'parameters': {
'timeseriesA': {
'name': 'nameA',
'unit': 'USD',
'data': [10, 20, 30, 40]
},
'timeseriesB': {
'name': 'nameB',
'unit': 'EUR',
'data': [60, 30, 40, 50]
}
}
}
data: Data = Data.fromDict(d)
if __name__ == '__main__':
main()
在本例中,dacite将忽略"timeseriesB“,但应将其作为”参数“数据集的字段添加。
发布于 2022-03-04 05:21:28
通常,在定义了类之后,动态地将字段添加到dataclass并不是一个好的实践。但是,由于源dict
对象中字段的动态性质,这为在数据集中使用dict
提供了一个很好的用例。
下面是一个使用dict
字段处理源对象中键的动态映射的简单示例,它使用dataclass-wizard
,它也是一个类似的JSON序列化库。下面概述的方法处理dict对象中的无关数据,例如timeseriesB
。
from __future__ import annotations
from dataclasses import dataclass
from dataclass_wizard import JSONWizard
@dataclass(frozen=True)
class Data(JSONWizard):
parameters: dict[str, TimeSeries]
@dataclass(frozen=True)
class TimeSeries:
name: str
unit: str
data: list[float] | None
data: dict = {
'parameters': {
'timeseriesA': {
'name': 'nameA',
'unit': 'USD',
'data': [10, 20, 30, 40]
},
'timeseriesB': {
'name': 'nameB',
'unit': 'EUR',
'data': [60, 30, 40, 50]
}
}
}
def main():
# deserialize from dict
d = Data.from_dict(data)
print(d.parameters['timeseriesB'].unit) # EUR
print(repr(d))
# Data(parameters={'timeseriesA': TimeSeries(name='nameA', unit='USD', data=[10.0, 20.0, 30.0, 40.0]),
# 'timeseriesB': TimeSeries(name='nameB', unit='EUR', data=[60.0, 30.0, 40.0, 50.0])})
if __name__ == '__main__':
main()
诚然,dataclass-wizard
并不像dacite
那样执行严格的类型检查,而是在可能的情况下执行隐式类型强制,如str
到带注释的int
。也许结果是,它的整体速度要快得多;另一个好的方面是序列化比内置的dataclasses.asdict
还要快一些:-)
以下是一些快速测试:
from dataclasses import asdict, dataclass
from typing import Dict, List, Optional
from dacite import from_dict
from dataclass_wizard import JSONWizard
from timeit import timeit
@dataclass(frozen=True)
class TimeSeries:
name: str
unit: str
data: Optional[List[float]]
@dataclass(frozen=True)
class Parameters:
timeseriesA: TimeSeries
@dataclass(frozen=True)
class Data:
parameters: Parameters
@classmethod
def fromDict(cls, data: Dict) -> 'Data':
return from_dict(cls, data)
def toDict(self) -> Dict:
return asdict(self)
@dataclass(frozen=True)
class ParametersWizard:
# renamed because default key transform is `camelCase` -> `snake_case`
timeseries_a: TimeSeries
@dataclass(frozen=True)
class DataWizard(JSONWizard):
# enable debug mode in case of incorrect types etc.
class _(JSONWizard.Meta):
debug_enabled = True
parameters: ParametersWizard
data: Dict = {
'parameters': {
'timeseriesA': {
'name': 'nameA',
'unit': 'USD',
'data': [10, 20, 30, 40]
},
'timeseriesB': {
'name': 'nameB',
'unit': 'EUR',
'data': [60, 30, 40, 50]
}
}
}
def main():
n = 10_000
print(f"From Dict: {timeit('Data.fromDict(data)', globals=globals(), number=n):.3f}")
print(f"From Dict (Wiz): {timeit('DataWizard.from_dict(data)', globals=globals(), number=n):.3f}")
data_1: Data = Data.fromDict(data)
data_wiz: Data = DataWizard.from_dict(data)
g = globals().copy()
g.update(locals())
print(f"To Dict: {timeit('data_1.toDict()', globals=g, number=n):.3f}")
print(f"To Dict (Wiz): {timeit('data_wiz.to_dict()', globals=g, number=n):.3f}")
if __name__ == '__main__':
main()
结果,在我的电脑(Windows)上:
From Dict: 1.663
From Dict (Wiz): 0.059
To Dict: 0.105
To Dict (Wiz): 0.057
https://stackoverflow.com/questions/71334683
复制相似问题