복붙노트

[PYTHON] 파이썬에서는 YAML 매핑을 OrderedDicts로 어떻게로드 할 수 있습니까?

PYTHON

파이썬에서는 YAML 매핑을 OrderedDicts로 어떻게로드 할 수 있습니까?

필자는 PyYAML의 로더가 바닐라 dict와 현재 사용되는 쌍 목록 대신 Python 2.7 + OrderedDict 형식으로 매핑 (및 순서가 매겨진 매핑)을로드하도록하고 싶습니다.

그렇게하는 가장 좋은 방법은 무엇입니까?

해결법

  1. ==============================

    1.업데이트 : 파이썬 3.6 +에 대해서는 새로운 dict 구현으로 인해 특별한 것을 필요로하지 않을 것입니다 (CPython 구현 세부 사항은 현재 고려하고 있습니다).

    업데이트 : 파이썬 3.6 +에 대해서는 새로운 dict 구현으로 인해 특별한 것을 필요로하지 않을 것입니다 (CPython 구현 세부 사항은 현재 고려하고 있습니다).

    나는 단순함 때문에 @James의 해결책을 좋아한다. 그러나 기본 yaml.Loader 클래스를 변경하면 문제가되는 부작용이 발생할 수 있습니다. 특히 라이브러리 코드를 작성할 때 이것은 나쁜 생각입니다. 또한 yaml.safe_load ()에서는 직접 작동하지 않습니다.

    다행히도 솔루션은 많은 노력을하지 않고도 개선 될 수 있습니다.

    import yaml
    from collections import OrderedDict
    
    def ordered_load(stream, Loader=yaml.Loader, object_pairs_hook=OrderedDict):
        class OrderedLoader(Loader):
            pass
        def construct_mapping(loader, node):
            loader.flatten_mapping(node)
            return object_pairs_hook(loader.construct_pairs(node))
        OrderedLoader.add_constructor(
            yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
            construct_mapping)
        return yaml.load(stream, OrderedLoader)
    
    # usage example:
    ordered_load(stream, yaml.SafeLoader)
    

    직렬화를 위해 나는 명백한 일반화를 알지 못하지만 최소한 이것은 어떤 부작용도 없어야한다.

    def ordered_dump(data, stream=None, Dumper=yaml.Dumper, **kwds):
        class OrderedDumper(Dumper):
            pass
        def _dict_representer(dumper, data):
            return dumper.represent_mapping(
                yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
                data.items())
        OrderedDumper.add_representer(OrderedDict, _dict_representer)
        return yaml.dump(data, stream, OrderedDumper, **kwds)
    
    # usage:
    ordered_dump(data, Dumper=yaml.SafeDumper)
    
  2. ==============================

    2.yaml 모듈을 사용하면 Python 객체를 텍스트로 변환하고 '생성자'를 사용하여 프로세스를 되돌릴 수있는 사용자 지정 '표현 자'를 지정할 수 있습니다.

    yaml 모듈을 사용하면 Python 객체를 텍스트로 변환하고 '생성자'를 사용하여 프로세스를 되돌릴 수있는 사용자 지정 '표현 자'를 지정할 수 있습니다.

    _mapping_tag = yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG
    
    def dict_representer(dumper, data):
        return dumper.represent_dict(data.iteritems())
    
    def dict_constructor(loader, node):
        return collections.OrderedDict(loader.construct_pairs(node))
    
    yaml.add_representer(collections.OrderedDict, dict_representer)
    yaml.add_constructor(_mapping_tag, dict_constructor)
    
  3. ==============================

    3.oyaml은 dict ordering을 유지하는 PyYAML을 대체합니다. 파이썬 2와 파이썬 3 모두 지원됩니다. 그냥 oyaml을 설치하고 아래와 같이 가져 오기하십시오 :

    oyaml은 dict ordering을 유지하는 PyYAML을 대체합니다. 파이썬 2와 파이썬 3 모두 지원됩니다. 그냥 oyaml을 설치하고 아래와 같이 가져 오기하십시오 :

    import oyaml as yaml
    

    덤프 /로드 할 때 스크류 - 업 맵핑으로 더 이상 짜증을 내지 않습니다.

    참고 : 나는 oyaml의 저자입니다.

  4. ==============================

    4.ruamel.yaml은 PyYAML을 대체하는 드롭입니다 (면책 조항 : 나는 그 패키지의 저자입니다). 매핑의 순서를 유지하는 것은 2015 년 첫 번째 버전 (0.1)에 추가 된 항목 중 하나였습니다. 사전의 순서를 유지할뿐만 아니라 주석, 앵커 이름, 태그를 보존하고 YAML 1.2를 지원합니다 사양 (2009 년 출시)

    ruamel.yaml은 PyYAML을 대체하는 드롭입니다 (면책 조항 : 나는 그 패키지의 저자입니다). 매핑의 순서를 유지하는 것은 2015 년 첫 번째 버전 (0.1)에 추가 된 항목 중 하나였습니다. 사전의 순서를 유지할뿐만 아니라 주석, 앵커 이름, 태그를 보존하고 YAML 1.2를 지원합니다 사양 (2009 년 출시)

    사양에서는 순서가 보장되지는 않지만 물론 YAML 파일에 순서가 있고 적절한 파서가이를 유지하고 순서를 유지하는 객체를 투명하게 생성 할 수 있다고합니다. 올바른 파서, 로더 및 덤퍼를 선택하기 만하면됩니다 .¹ :

    import sys
    import ruamel.yaml as yaml
    
    yaml_str = """\
    3: abc
    conf:
        10: def
        3: gij     # h is missing
    more:
    - what
    - else
    """
    
    data = yaml.load(yaml_str, Loader=yaml.RoundTripLoader)
    data['conf'][10] = 'klm'
    data['conf'][3] = 'jig'
    yaml.dump(data, sys.stdout, Dumper=yaml.RoundTripDumper)
    

    당신을 줄 것입니다 :

    3: abc
    conf:
      10: klm
      3: jig       # h is missing
    more:
    - what
    - else
    

    데이터는 Dict처럼 작동하는 CommentedMap 유형이지만 덤프 될 때까지 보존되는 추가 정보가 있습니다 (보존 된 주석 포함).

  5. ==============================

    5.참고 : CLoader 및 CDumpers를 구현하는 다음 답변을 기반으로하는 라이브러리가 있습니다. Phynix / yamlloader

    참고 : CLoader 및 CDumpers를 구현하는 다음 답변을 기반으로하는 라이브러리가 있습니다. Phynix / yamlloader

    나는 이것이 이것이 할 수있는 가장 좋은 방법이라는 것을 매우 의심 스럽다. 그러나 이것이 내가 생각해내는 방식이며, 그것이 효과가있다. 요점으로도 제공됩니다.

    import yaml
    import yaml.constructor
    
    try:
        # included in standard lib from Python 2.7
        from collections import OrderedDict
    except ImportError:
        # try importing the backported drop-in replacement
        # it's available on PyPI
        from ordereddict import OrderedDict
    
    class OrderedDictYAMLLoader(yaml.Loader):
        """
        A YAML loader that loads mappings into ordered dictionaries.
        """
    
        def __init__(self, *args, **kwargs):
            yaml.Loader.__init__(self, *args, **kwargs)
    
            self.add_constructor(u'tag:yaml.org,2002:map', type(self).construct_yaml_map)
            self.add_constructor(u'tag:yaml.org,2002:omap', type(self).construct_yaml_map)
    
        def construct_yaml_map(self, node):
            data = OrderedDict()
            yield data
            value = self.construct_mapping(node)
            data.update(value)
    
        def construct_mapping(self, node, deep=False):
            if isinstance(node, yaml.MappingNode):
                self.flatten_mapping(node)
            else:
                raise yaml.constructor.ConstructorError(None, None,
                    'expected a mapping node, but found %s' % node.id, node.start_mark)
    
            mapping = OrderedDict()
            for key_node, value_node in node.value:
                key = self.construct_object(key_node, deep=deep)
                try:
                    hash(key)
                except TypeError, exc:
                    raise yaml.constructor.ConstructorError('while constructing a mapping',
                        node.start_mark, 'found unacceptable key (%s)' % exc, key_node.start_mark)
                value = self.construct_object(value_node, deep=deep)
                mapping[key] = value
            return mapping
    
  6. ==============================

    6.업데이트 : 라이브러리는 yamlloader (yamlordereddictloader를 기반으로 함)를 사용하여 더 이상 사용되지 않습니다.

    업데이트 : 라이브러리는 yamlloader (yamlordereddictloader를 기반으로 함)를 사용하여 더 이상 사용되지 않습니다.

    방금 파이썬 라이브러리 (https://pypi.python.org/pypi/yamlordereddictloader/0.1.1)를 찾았습니다.이 라이브러리는이 질문에 대한 답변을 기반으로 작성되었으며 매우 사용하기 쉽습니다.

    import yaml
    import yamlordereddictloader
    
    datas = yaml.load(open('myfile.yml'), Loader=yamlordereddictloader.Loader)
    
  7. ==============================

    7.Python 2.7 용 PyYaml 설치에서 __init__.py, constructor.py 및 loader.py를 업데이트했습니다. 이제 load 명령에 대한 object_pairs_hook 옵션을 지원합니다. 내가 만든 변경 사항의 차이점은 다음과 같습니다.

    Python 2.7 용 PyYaml 설치에서 __init__.py, constructor.py 및 loader.py를 업데이트했습니다. 이제 load 명령에 대한 object_pairs_hook 옵션을 지원합니다. 내가 만든 변경 사항의 차이점은 다음과 같습니다.

    __init__.py
    
    $ diff __init__.py Original
    64c64
    < def load(stream, Loader=Loader, **kwds):
    ---
    > def load(stream, Loader=Loader):
    69c69
    <     loader = Loader(stream, **kwds)
    ---
    >     loader = Loader(stream)
    75c75
    < def load_all(stream, Loader=Loader, **kwds):
    ---
    > def load_all(stream, Loader=Loader):
    80c80
    <     loader = Loader(stream, **kwds)
    ---
    >     loader = Loader(stream)
    
    constructor.py
    
    $ diff constructor.py Original
    20,21c20
    <     def __init__(self, object_pairs_hook=dict):
    <         self.object_pairs_hook = object_pairs_hook
    ---
    >     def __init__(self):
    27,29d25
    <     def create_object_hook(self):
    <         return self.object_pairs_hook()
    <
    54,55c50,51
    <         self.constructed_objects = self.create_object_hook()
    <         self.recursive_objects = self.create_object_hook()
    ---
    >         self.constructed_objects = {}
    >         self.recursive_objects = {}
    129c125
    <         mapping = self.create_object_hook()
    ---
    >         mapping = {}
    400c396
    <         data = self.create_object_hook()
    ---
    >         data = {}
    595c591
    <             dictitems = self.create_object_hook()
    ---
    >             dictitems = {}
    602c598
    <             dictitems = value.get('dictitems', self.create_object_hook())
    ---
    >             dictitems = value.get('dictitems', {})
    
    loader.py
    
    $ diff loader.py Original
    13c13
    <     def __init__(self, stream, **constructKwds):
    ---
    >     def __init__(self, stream):
    18c18
    <         BaseConstructor.__init__(self, **constructKwds)
    ---
    >         BaseConstructor.__init__(self)
    23c23
    <     def __init__(self, stream, **constructKwds):
    ---
    >     def __init__(self, stream):
    28c28
    <         SafeConstructor.__init__(self, **constructKwds)
    ---
    >         SafeConstructor.__init__(self)
    33c33
    <     def __init__(self, stream, **constructKwds):
    ---
    >     def __init__(self, stream):
    38c38
    <         Constructor.__init__(self, **constructKwds)
    ---
    >         Constructor.__init__(self)
    
  8. ==============================

    8.5 년 전에 개설 된 주제에 PyYAML 티켓이 있습니다. 이 질문에 대한 링크를 포함하여 몇 가지 관련 링크가 있습니다. 개인적으로 317164를 골라서 포함 된 구현이 아닌 Python 2.7에서 OrderedDict를 사용하기 위해 약간 수정했습니다 (컬렉션 가져 오기 OrderedDict에서 클래스를 대체했습니다).

    5 년 전에 개설 된 주제에 PyYAML 티켓이 있습니다. 이 질문에 대한 링크를 포함하여 몇 가지 관련 링크가 있습니다. 개인적으로 317164를 골라서 포함 된 구현이 아닌 Python 2.7에서 OrderedDict를 사용하기 위해 약간 수정했습니다 (컬렉션 가져 오기 OrderedDict에서 클래스를 대체했습니다).

  9. ==============================

    9.지도의 중복 된 최상위 키를 확인하는 간단한 솔루션이 있습니다.

    지도의 중복 된 최상위 키를 확인하는 간단한 솔루션이 있습니다.

    import yaml
    import re
    from collections import OrderedDict
    
    def yaml_load_od(fname):
        "load a yaml file as an OrderedDict"
        # detects any duped keys (fail on this) and preserves order of top level keys
        with open(fname, 'r') as f:
            lines = open(fname, "r").read().splitlines()
            top_keys = []
            duped_keys = []
            for line in lines:
                m = re.search(r'^([A-Za-z0-9_]+) *:', line)
                if m:
                    if m.group(1) in top_keys:
                        duped_keys.append(m.group(1))
                    else:
                        top_keys.append(m.group(1))
            if duped_keys:
                raise Exception('ERROR: duplicate keys: {}'.format(duped_keys))
        # 2nd pass to set up the OrderedDict
        with open(fname, 'r') as f:
            d_tmp = yaml.load(f)
        return OrderedDict([(key, d_tmp[key]) for key in top_keys])
    
  10. from https://stackoverflow.com/questions/5121931/in-python-how-can-you-load-yaml-mappings-as-ordereddicts by cc-by-sa and MIT license