使用 dacite.from_dict 动态添加数据类字段
Adding dataclass fields dynamically with dacite.from_dict
我正在使用 英安岩 将 Python 字典转换为数据类。有没有办法动态地向数据类添加字段?如下例所示,数据类“Parameters”仅定义了一个时间序列“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()
在此示例中,英安岩 将忽略“timeseriesB”,但应添加为“参数”数据类的字段。
一般来说,在定义 class 之后动态添加 字段 到数据 class 不是好的做法。但是,由于源 dict
中字段的 动态 性质,这确实为在数据 class 中使用 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
那样执行 strict 类型检查,而是执行 implicit在可能的情况下,键入强制转换,例如 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
我正在使用 英安岩 将 Python 字典转换为数据类。有没有办法动态地向数据类添加字段?如下例所示,数据类“Parameters”仅定义了一个时间序列“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()
在此示例中,英安岩 将忽略“timeseriesB”,但应添加为“参数”数据类的字段。
一般来说,在定义 class 之后动态添加 字段 到数据 class 不是好的做法。但是,由于源 dict
中字段的 动态 性质,这确实为在数据 class 中使用 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
那样执行 strict 类型检查,而是执行 implicit在可能的情况下,键入强制转换,例如 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