Im Folgenden finden Sie die CPython-Implementierung asdict
- oder speziell die interne rekursive Hilfsfunktion _asdict_inner, die verwendet wird:
def _asdict_inner(obj, dict_factory):
if _is_dataclass_instance(obj):
result = []
for f in fields(obj):
value = _asdict_inner(getattr(obj, f.name), dict_factory)
result.append((f.name, value))
return dict_factory(result)
elif isinstance(obj, tuple) and hasattr(obj, '_fields'):
return type(obj)(*[_asdict_inner(v, dict_factory) for v in obj])
elif isinstance(obj, (list, tuple)):
return type(obj)(_asdict_inner(v, dict_factory) for v in obj)
elif isinstance(obj, dict):
return type(obj)((_asdict_inner(k, dict_factory),
_asdict_inner(v, dict_factory))
for k, v in obj.items())
else:
return copy.deepcopy(obj)
asdictRuft einfach das Obige mit einigen Behauptungen auf und dict_factory=dictstandardmäßig.
Wie kann dies angepasst werden, um ein Ausgabewörterbuch mit der erforderlichen Typkennzeichnung zu erstellen, wie in den Kommentaren erwähnt?
1. Typinformationen hinzufügen
Mein Versuch bestand darin, einen benutzerdefinierten Return-Wrapper zu erstellen, der Folgendes erbt dict:
class TypeDict(dict):
def __init__(self, t, *args, **kwargs):
super(TypeDict, self).__init__(*args, **kwargs)
if not isinstance(t, type):
raise TypeError("t must be a type")
self._type = t
@property
def type(self):
return self._type
Im ursprünglichen Code muss nur die erste Klausel geändert werden, um diesen Wrapper zu verwenden, da die anderen Klauseln nur Container mit dataclass-es behandeln:
def _todict_inner(obj):
if is_dataclass_instance(obj):
result = []
for f in fields(obj):
value = _todict_inner(getattr(obj, f.name))
result.append((f.name, value))
return TypeDict(type(obj), result)
elif isinstance(obj, tuple) and hasattr(obj, '_fields'):
return type(obj)(*[_todict_inner(v) for v in obj])
elif isinstance(obj, (list, tuple)):
return type(obj)(_todict_inner(v) for v in obj)
elif isinstance(obj, dict):
return type(obj)((_todict_inner(k), _todict_inner(v))
for k, v in obj.items())
else:
return copy.deepcopy(obj)
Importe:
from dataclasses import dataclass, fields, is_dataclass
from typing import *
import copy
Verwendete Funktionen:
def is_dataclass_instance(obj):
return is_dataclass(obj) and not is_dataclass(obj.type)
def todict(obj):
if not is_dataclass_instance(obj):
raise TypeError("todict() should be called on dataclass instances")
return _todict_inner(obj)
Tests mit den Beispieldatenklassen:
c = C([Point(0, 0), Point(10, 4)])
print(c)
cd = todict(c)
print(cd)
print(cd.type)
Ergebnisse sind wie erwartet.
2. Zurückkonvertieren in a dataclass
Die von verwendete rekursive Routine asdictkann mit einigen relativ geringfügigen Änderungen für den umgekehrten Prozess wiederverwendet werden:
def _fromdict_inner(obj):
if is_dataclass_dict(obj):
result = {}
for name, data in obj.items():
result[name] = _fromdict_inner(data)
return obj.type(**result)
elif isinstance(obj, (list, tuple)):
return type(obj)(_fromdict_inner(v) for v in obj)
elif isinstance(obj, dict):
return type(obj)((_fromdict_inner(k), _fromdict_inner(v))
for k, v in obj.items())
else:
return copy.deepcopy(obj)
Verwendete Funktionen:
def is_dataclass_dict(obj):
return isinstance(obj, TypeDict)
def fromdict(obj):
if not is_dataclass_dict(obj):
raise TypeError("fromdict() should be called on TypeDict instances")
return _fromdict_inner(obj)
Prüfung:
c = C([Point(0, 0), Point(10, 4)])
cd = todict(c)
cf = fromdict(cd)
print(c)
print(cf)
Wieder wie erwartet.