2017-10-01 4 views
2

RangeDict을 사용하여 범위가 포함 된 사전을 만듭니다. Pickle을 사용하면 파일에 쉽게 기록되고 나중에 읽을 수 있습니다.Python에서 YAML 또는 JSON을 사용하여 RangeDict 직렬화

import pickle 
from rangedict import RangeDict 

rngdct = RangeDict() 
rngdct[(1, 9)] = \ 
    {"Type": "A", "Series": "1"} 
rngdct[(10, 19)] = \ 
    {"Type": "B", "Series": "1"} 

with open('rangedict.pickle', 'wb') as f: 
    pickle.dump(rngdct, f) 

는 그러나, 나는 대부분의 사람들이 싫어하는 것 때문에 대신 피클의 YAML (또는 JSON YAML이 작동하지 않을 경우를 ...) 사용하려면 (그들이 이해 그래서 나는 사람이 읽을 수있는 파일을 저장할

기본적으로 yaml을 호출하고 모드로 파일을 여는 코드는 'wb'이 아니라 쓰기 편이 트릭이지만 다른 스크립트에서 파일을 읽을 때 오류 :

File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/yaml/constructor.py", line 129, in construct_mapping 
value = self.construct_object(value_node, deep=deep) 
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/yaml/constructor.py", line 61, in construct_object 
"found unconstructable recursive node", node.start_mark) 
yaml.constructor.ConstructorError: found unconstructable recursive node 

이리. 어떻게 rangedict 객체를 직렬화하고 원래 형식으로 다시 읽을 수 있습니까?

+0

나는'NSStock'가 오타라고 생각합니다. 그렇지 않다면 당신의 예제에 그 정의를 추가하십시오. – Anthon

+0

맞아요! 죄송합니다. 변수 이름을 변경했지만 잊어 버렸습니다. 발언 해줘서 고마워! @Anthon –

답변

0

TL; 나는 어떤 사람들은 (절인 객체의 클래스가 다른 파일을 이동할 때) 코드를 리팩토링 할 때, 그것은 확실히 약간의 두통을 줄 수 pickle 싫어 확신 코드


작업이 대답의 바닥으로 건너 뜁니다. 그러나 더 큰 문제는 장아찌가 안전하지 않다는 것입니다. 단지 YAML이 당신이 사용했던 방식입니다. 것 (= 0 rngdct, F, 프로토콜)

pickle.dump :

그것은 당신이 더 많은 읽을 수 protocol level 0에 피클 수 없음을주의하는 것이 재미뿐만 (파이썬 3에서 기본 프로토콜 버전 3)이다 던져 :

TypeError: a class that defines slots without defining getstate cannot be pickled

RangeDict 모듈/클래스는, 조금 최소한의 때문은 또한 어떤 쇼 (또는 오히려하지 않습니다) 당신이하려고하면 :

print(rngdict) 
단지 {}

당신은 아마 PyYAML dump() 루틴을 사용

인쇄됩니다

(그리고 그것의 대응, 안전하지 않은, load()). 파이썬이 일반적인 파이썬 클래스를 덤프 할 수는 있지만 파이썬 3.0 이전에 또는 대략 동시에 구현되었다는 것을 알아야합니다. (그리고 Python 3 지원은 나중에 구현되었다). 그리고 YAML 파서가 pickle이하는 정확한 정보를 덤프하고로드 할 이유는 없지만 pickle 지원 루틴에 연결할 수는 있지만 Python 3 특정 산세 프로토콜에 대한 정보에는 포함되지 않습니다. 그것은 잠재적으로 안전하지 않은로드하게하고 YAML 개체가 효율적으로 그 피투성이의 세부 사항 모두 포함 : 정말 이해가되지 않습니다 YAML을 사용

RangeDict 객체에 대한 특정 representer (그리고 생성자)없이 어떤 방법, .당신이 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} 

는 YAML에 읽을 수 표현 될 경우 IMO 다음에

때문에 키로서 사용되는 시퀀스의
!rangedict 
[1, 9]: 
    Type: A 
    Series: '1' 
[10, 19]: 
    Type: B 
    Series: '1' 

,이 PyYAML에 의해로드 할 수없는 주요 수정 파서. 그러나 다행스럽게도, 그 수정이 ruamel.yaml에 통합 된

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'] 
: "모두가"당신이해야 할 (면책 조항 나는 그 패키지의 저자), 그래서 적절한 representer와 생성자 (클래스) 방법을 제공하는 서브 클래스 RangeDict입니다

buf.getvalue()은 정확히 이전에 읽을 수있는 표현입니다. 당신이 (당신이 하드 코드 RangeDict을 가지고 몇 가지 라이브러리를 사용하기 때문에 즉 서브 클래스 수 없습니다) RangeDict 자체를 덤핑 처리해야하는 경우

는, 당신은 이식/monkeypatching 기준에 대한 특성 및 MyRangeDict 직접 RangeDict의 방법을 추가 할 수 있습니다.

+0

이것은 실제로 유효한 대답입니다. YAML 라이브러리는 트릭을 완벽하게 처리하고 사람이 읽을 수있는 출력 파일을 생성합니다. "왕복 동등한"부분은 나에게 조금 그늘지만, 예외 범위에 속하지 않으므로 내 RangeDict가 올바른 형식/데이터를 가지고 있다고 생각하십니까? –

+0

그 동등한 부분은 내부 구조에 의존합니다. 단지 모든 범위를 테스트하고 생성 된'rngdct '가 그 범위 ('x [0]')의 첫 번째 값과 동일한 값을 가지도록합니다.'data '. 그냥 무언가를'load() '하지 말고 오류를 없애고, 처음부터 시작한 것과 완전히 다른 무언가로 끝내십시오. – Anthon

관련 문제