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)
asdict
Ruft einfach das Obige mit einigen Behauptungen auf und dict_factory=dict
standardmäß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 asdict
kann 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.