TL; DR; Direkt zum Ende der Antwort für die Code-Arbeits
Ich bin sicher, dass einige Leute pickle
hassen, es kann sicherlich einigen Kopfschmerzen geben, wenn der Code Refactoring (wenn die Klassen von eingelegten Objekten auf verschiedene Dateien zu verschieben). Aber das größere Problem ist, dass Gurke unsicher ist, nur ein YAML ist in der Art, wie Sie es verwendet haben.
Es ist für interessant, dass Sie nicht auf den besser lesbar protocol level 0 Pickles (3 Standard in Python-Protokoll Version 3) als:
pickle.dump (rngdct, f, protocol = 0) wird werfen:
TypeError: a class that defines slots without defining getstate cannot be pickled
Dies liegt daran, die RangeDict
Modul/Klasse ein bisschen minimalistisch ist, was auch zeigt (oder eher nicht), wenn Sie versuchen zu tun:
print(rngdict)
die gerade {}
Sie wahrscheinlich die dump()
Routine PyYAML verwendet gedruckt wird (und die entsprechende, unsicher, load()
). Und obwohl dies generische Python-Klassen ausgeben kann, müssen Sie feststellen, dass das vor oder ungefähr zur gleichen Zeit wie Python 3.0 implementiert wurde. (und Python 3 Unterstützung wurde später implementiert). Und obwohl es keinen Grund gibt, warum ein YAML-Parser die genauen Informationen, die pickle
sind, ablegen und laden kann, greift er nicht in die pickle
Support-Routinen ein (obwohl es möglich war) und sicherlich nicht in die Informationen für die Python 3-spezifischen Beiz-Protokolle.
Jede Art und Weise, ohne einen bestimmten Representer (und Konstruktor) für RangeDict
Objekte, YAML verwendet, nicht wirklich keinen Sinn: es macht potenziell unsicheren Laden und Ihre YAML umfassen alle der blutigen Details, die das Objekt effizienter zu gestalten .Wenn Sie yaml.dump()
tun:
!!python/object:rangedict.RangeDict
_root: &id001 !!python/object/new:rangedict.Node
state: !!python/tuple
- null
- color: 0
left: null
parent: null
r: !!python/tuple [1, 9]
right: !!python/object/new:rangedict.Node
state: !!python/tuple
- null
- color: 1
left: null
parent: *id001
r: !!python/tuple [10, 19]
right: null
value: {Series: '1', Type: B}
value: {Series: '1', Type: A}
Wo IMO eine lesbar Darstellung in YAML wäre:
!rangedict
[1, 9]:
Type: A
Series: '1'
[10, 19]:
Type: B
Series: '1'
Wegen der Sequenzen als Schlüssel verwendet, kann dies nicht durch PyYAML ohne größere Änderungen an der geladen werden Parser. Aber zum Glück haben sich diese Änderungen aufgenommen worden in ruamel.yaml
(Disclaimer: Ich bin der Autor dieses Pakets), so „alle“ Sie tun müssen, ist Unterklasse RangeDict
geeignet Representer und Konstruktor (Klassen-) Methoden zur Verfügung zu stellen:
import io
import ruamel.yaml
from rangedict import RangeDict
class MyRangeDict(RangeDict):
yaml_tag = u'!rangedict'
def _walk(self, cur):
# walk tree left -> parent -> right
if cur.left:
for x in self._walk(cur.left):
yield x
yield cur.r
if cur.right:
for x in self._walk(cur.right):
yield x
@classmethod
def to_yaml(cls, representer, node):
d = ruamel.yaml.comments.CommentedMap()
for x in node._walk(node._root):
d[ruamel.yaml.comments.CommentedKeySeq(x)] = node[x[0]]
return representer.represent_mapping(cls.yaml_tag, d)
@classmethod
def from_yaml(cls, constructor, node):
d = cls()
for x, y in node.value:
x = constructor.construct_object(x, deep=True)
y = constructor.construct_object(y, deep=True)
d[x] = y
return d
rngdct = MyRangeDict()
rngdct[(1, 9)] = \
{"Type": "A", "Series": "1"}
rngdct[(10, 19)] = \
{"Type": "B", "Series": "1"}
yaml = ruamel.yaml.YAML()
yaml.register_class(MyRangeDict) # tell the yaml instance about this class
buf = io.StringIO()
yaml.dump(rngdct, buf)
data = yaml.load(buf.getvalue())
# test for round-trip equivalence:
for x in data._walk(data._root):
for y in range(x[0], x[1]+1):
assert data[y]['Type'] == rngdct[y]['Type']
assert data[y]['Series'] == rngdct[y]['Series']
Die buf.getvalue()
ist genau die lesbare Darstellung, die vorher gezeigt wurde.
Wenn Sie mit Dumping RangeDict
selbst zu tun haben (das heißt nicht Unterklasse, weil Sie einige Bibliothek verwenden, die RangeDict
fest einprogrammiert hat), dann können Sie das Attribut und Methoden der MyRangeDict
direkt an RangeDict
durch Pfropfen/monkeypatching hinzufügen.
Ich denke, der 'NSStock' war ein Tippfehler, wenn nicht, füge bitte seine Definition zu deinem Beispiel hinzu. – Anthon
Das stimmt! Sorry dafür, ich habe die Variablen umbenannt, aber diese vergessen. Danke für die Bemerkung! @ Anthon –