TL;ДР; Перейдите к нижней части этого ответа для рабочего кода
Я уверен, что некоторые люди ненавидят pickle
, это, безусловно, может вызвать головную боль при рефакторинге кода (когда классы маринованных объектов перемещаются в другие файлы). Но большая проблема заключается в том, что pickle небезопасен, просто YAML работает так, как вы его использовали.
Интересно отметить, что вы не можете перейти к более читаемому уровень протокола 0 (по умолчанию в Python 3 используется протокол версии 3):
pickle.dump(rngdct, f, protocol=0) выдаст:
TypeError: класс, который определяет слоты без определения getstate, не может быть обработан.
Это связано с тем, что модуль/класс RangeDict
немного минималистичный, что также отображается (или, скорее, не отображается), если вы пытаетесь сделать:
print(rngdict)
который просто напечатает {}
Вероятно, вы использовали подпрограмму PyYAML dump()
(и соответствующую ей небезопасную load()
). И хотя это может вывести общие классы Python, вы должны понимать, что это было реализовано до или примерно в то же время, что и Python 3.0. (и поддержка Python 3 была реализована позже). И хотя нет причин, по которым синтаксический анализатор YAML может выгружать и загружать точную информацию, которую делает pickle
, он не подключается к подпрограммам поддержки pickle
(хотя может) и, конечно же, не к информации для конкретных протоколов травления Python 3.
В любом случае, без специального репрезентатора (и конструктора) для объектов RangeDict
использование YAML на самом деле не имеет никакого смысла: это делает загрузку потенциально небезопасной, а ваш YAML включает все кровавые детали, которые делают объект эффективный. Если вы сделаете yaml.dump()
:
!!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}
Где IMO удобочитаемое представление в YAML будет:
!rangedict
[1, 9]:
Type: A
Series: '1'
[10, 19]:
Type: B
Series: '1'
Из-за последовательностей, используемых в качестве ключей, PyYAML не может загрузить их без серьезных изменений в синтаксическом анализаторе. Но, к счастью, эти изменения были включены в ruamel.yaml
(отказ от ответственности: я являюсь автором этот пакет), поэтому «все», что вам нужно сделать, это подкласс RangeDict
, чтобы предоставить подходящие методы представления и конструктора (класса):
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']
buf.getvalue()
— это точно читаемое представление, показанное ранее.
Если вам приходится иметь дело с дампом самого RangeDict
(т. е. вы не можете создавать подклассы, потому что используете какую-то библиотеку, в которой RangeDict
жестко запрограммировано), то вы можете добавить атрибут и методы MyRangeDict
непосредственно в RangeDict
путем прививки/обезьяны.
person
Anthon
schedule
03.10.2017
NSStock
было опечаткой, если нет, добавьте его определение в свой пример. - person Anthon   schedule 03.10.2017