rom*_*inf 4 python field dynamic python-3.x python-dataclasses
我正在编写一个访问REST API的库。它返回带有用户对象的json。我将其转换为dict,然后将其转换为dataclass对象。问题在于,并非所有字段都是固定的。我想动态添加其他字段(在我的数据类中未指定)。我可以简单地为我的对象分配值,但是它们不会出现在对象表示中,并且dataclasses.asdict函数也不会将它们添加到结果字典中:
from dataclasses import asdict, dataclass
@dataclass
class X:
i: int
x = X(i=42)
x.s = 'text'
x
# X(i=42)
x.s
# 'text'
asdict(x)
# {'i': 42}
Run Code Online (Sandbox Code Playgroud)
rv.*_*tch 11
更新(6/22):现在已经是 2022 年中期了,我想我应该用我一直在尝试的全新方法来刷新我的答案。我很高兴地宣布我最近发布了一个快速、现代的库,名为dotwiz.
该库可以使用pipdotwiz安装:
pip install dotwiz\nRun Code Online (Sandbox Code Playgroud)\n这是我创建的一个小型帮助程序库,它使dict对象可以安全地通过点表示法访问 - 例如而a.b.c不是a[\'b\'][\'c\']. 从个人测试和基准来看,它实际上比类似的东西要快得多-make_dataclass下面有更多信息。
此外,还可以从DotWiz或进行子类化DotWizPlus,这样就可以从 IDE(例如 PyCharm)进行类型提示和自动完成提示。下面是一个简单的例子:
pip install dotwiz\nRun Code Online (Sandbox Code Playgroud)\n如果您仍然喜欢使用数据类来对数据进行建模,我在下面包含了我的原始答案,该答案与过去几年相比基本没有变化。
\n以下结果是在配备 M1 芯片、Python 3.10.4 和n=5000迭代的 Mac Pro 上测得的。
创建或实例化对象:
\n$ python -m timeit -n 5000 -s "from dotwiz import DotWiz" -c "DotWiz(i=42, s=\'text\')"\n5000 loops, best of 5: 425 nsec per loop\n\n$ python -m timeit -n 5000 -s "from dataclasses import make_dataclass" -c "X = make_dataclass(\'X\', [(\'i\', int), (\'s\', str)]); X(i=42, s=\'text\')"\n5000 loops, best of 5: 97.8 usec per loop\nRun Code Online (Sandbox Code Playgroud)\n这些时间可能被夸大了,但在这种特殊情况下,它DotWiz看起来比make_dataclass. 实际上,我想说平均速度大约快 100 倍。
通过点表示法访问键:
\n$ python -m timeit -n 5000 -s "from dotwiz import DotWiz" -s "dw = DotWiz(i=42, s=\'text\')" -c "dw.s.lower()" \n5000 loops, best of 5: 39.7 nsec per loop\n\n$ python -m timeit -n 5000 -s "from dataclasses import make_dataclass" -s "X = make_dataclass(\'X\', [(\'i\', int), (\'s\', str)])" -s "x = X(i=42, s=\'text\')" -c "x.s.lower()"\n5000 loops, best of 5: 39.9 nsec per loop\nRun Code Online (Sandbox Code Playgroud)\n访问属性或键的时间看起来基本相同。
\n将对象序列化为 JSON:
\n$ python -m timeit -n 5000 -s "import json" -s "from dotwiz import DotWiz" -s "dw = DotWiz(i=42, s=\'text\')" -c "json.dumps(dw)"\n5000 loops, best of 5: 1.1 usec per loop\n\n$ python -m timeit -n 5000 -s "import json" -s "from dotwiz import DotWiz" -s "dw = DotWiz(i=42, s=\'text\')" -c "json.dumps(dw.to_dict())"\n5000 loops, best of 5: 1.46 usec per loop\n\n$ python -m timeit -n 5000 -s "import json" -s "from dataclasses import asdict, make_dataclass" -s "X = make_dataclass(\'X\', [(\'i\', int), (\'s\', str)])" -s "x = X(i=42, s=\'text\')" -c "json.dumps(asdict(x))"\n5000 loops, best of 5: 2.87 usec per loop\nRun Code Online (Sandbox Code Playgroud)\n因此,与实例相比,序列化对象实际上看起来要 快 2.5 倍。DotWizdataclass
如前所述,标记为可选的字段应该可以解决该问题。如果没有,请考虑使用 中的属性dataclasses。是的,常规属性应该工作得足够好 - 尽管您必须在 中声明字段__post_init__,这有点不方便。
如果您想为属性设置默认值,以便在创建对象后立即访问 getter 效果很好,并且如果您还希望能够通过构造函数设置默认值,则可以使用称为字段属性的概念;一些库,例如dataclass-wizard提供了对此的全面支持。
\n用法示例:
\nfrom dataclasses import asdict, make_dataclass\n\nfrom dotwiz import DotWiz\n\n\nclass MyTypedWiz(DotWiz):\n # add attribute names and annotations for better type hinting!\n i: int\n s: str\n\n\ndw = MyTypedWiz(i=42, s=\'text\')\nprint(dw)\n# \xe2\x9c\xab(i=42, s=\'text\')\n\nprint(dw.to_dict())\n# {\'i\': 42, \'s\': \'text\'}\nRun Code Online (Sandbox Code Playgroud)\n免责声明:我是这个库的创建者(和维护者)。
\n您可以用来即时make_dataclass创建X:
X = make_dataclass('X', [('i', int), ('s', str)])
x = X(i=42, s='text')
asdict(x)
# {'i': 42, 's': 'text'}
Run Code Online (Sandbox Code Playgroud)
或作为派生类:
@dataclass
class X:
i: int
x = X(i=42)
x.__class__ = make_dataclass('Y', fields=[('s', str)], bases=(X,))
x.s = 'text'
asdict(x)
# {'i': 42, 's': 'text'}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1026 次 |
| 最近记录: |