पायथन में, आप ऑर्डरडिक के रूप में YAML मैपिंग को कैसे लोड कर सकते हैं?


128

मैं PyYAML के लोडर को मैपिंग लोड करने के लिए लोड करना चाहता हूं (और मैपिंग का आदेश दिया) Python 2.7+ ऑर्डर किए गए प्रकार में, वेनिला dictऔर वर्तमान में उपयोग किए जाने वाले जोड़ों की सूची के बजाय ।

ऐसा करने का सबसे अच्छा तरीका क्या है?

जवाबों:


147

अपडेट: अजगर 3.6+ में आपको नए तानाशाही कार्यान्वयन केOrderedDict कारण संभवतः इसकी आवश्यकता नहीं है तानाशाह कुछ समय के लिए pypy में उपयोग किया गया है (हालांकि अभी के लिए CPython कार्यान्वयन विवरण माना जाता है)।

अद्यतन: अजगर 3.7+ में, प्रमुख वस्तुओं के सम्मिलन-आदेश संरक्षण प्रकृति को पायथन भाषा कल्पना का आधिकारिक हिस्सा घोषित किया गया है , देखें कि नया क्या है पायथन 3.7

मुझे इसकी सरलता के लिए @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)

3
+1 - इसके लिए आपका बहुत-बहुत धन्यवाद, इसने मुझे बहुत परेशानी से बचाया।
नोबिलिस

2
यह कार्यान्वयन
रैंडी

1
@ कैंडी धन्यवाद। मैं उस परिदृश्य में पहले नहीं चला था, लेकिन अब मैंने इसे ठीक करने के लिए एक जुड़ाव जोड़ा (मुझे उम्मीद है)।
कोल्डफिक्स

9
@ArneBabenhauserheide मुझे यकीन नहीं है कि अगर PyPI काफी ऊपर है, लेकिन ruamel.yaml (मैं उस का लेखक हूं) पर एक नज़र डालते हैं अगर आपको लगता है कि यह करता है।
एंथन

1
@Anthon आपका रुचिकर। श्याम पुस्तकालय बहुत अच्छा काम करता है। उसके लिए धन्यवाद।
जनवरी Vlcinsky

56

यमल मॉड्यूल आपको प्रक्रिया को उलटने के लिए पायथन ऑब्जेक्ट्स को टेक्स्ट और 'कंस्ट्रक्टर्स' में बदलने के लिए कस्टम 'अभ्यावेदन' निर्दिष्ट करने की अनुमति देता है।

_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)

5
इस उत्तर के लिए कोई स्पष्टीकरण?
शुमन

1
या इससे भी बेहतर from six import iteritemsऔर फिर इसे बदल दें iteritems(data)ताकि यह पायथन 2 और 3 में समान रूप से अच्छी तरह से काम करे
Midnighter

5
ऐसा लगता है कि PyYAML ( represent_dictऔर DEFAULT_MAPPING_TAG) की अनिर्दिष्ट सुविधाओं का उपयोग किया जा रहा है । क्या यह इसलिए है क्योंकि दस्तावेज़ अपूर्ण है, या ये सुविधाएँ असमर्थित हैं और बिना सूचना के बदले जा सकती हैं?
aldel

3
ध्यान दें कि dict_constructorआपको कॉल करने की आवश्यकता loader.flatten_mapping(node)होगी या आप लोड नहीं कर पाएंगे <<: *...(सिंटैक्स को मर्ज करना)
एंथोनी सॉटाइल

@ brice-m-dempsey क्या आप अपने कोड का उपयोग करने के लिए कोई उदाहरण जोड़ सकते हैं? यह मेरे मामले में काम नहीं करता है (पायथन 3.7)
स्कैफ़

53

2018 विकल्प:

oyamlPyYAML के लिए एक ड्रॉप-इन रिप्लेसमेंट है जो तानाशाही आदेश को संरक्षित करता है। पायथन 2 और पायथन 3 दोनों समर्थित हैं। बस pip install oyaml, और नीचे दिखाए अनुसार आयात करें:

import oyaml as yaml

डंपिंग / लोडिंग के दौरान आप खराब-अप मैपिंग से परेशान नहीं होंगे।

नोट: मैं oyaml का लेखक हूं।


1
इसके लिए शुक्रिया! किसी कारण के लिए, पायथन 3.8 के साथ भी आदेश PyYaml के साथ सम्मान नहीं किया गया था। oyaml ने मेरे लिए इसे तुरंत हल कर दिया।
जॉन स्मिथ वैकल्पिक

26

2015 (और बाद में) विकल्प:

ruamel.yaml PyYAML के प्रतिस्थापन में एक गिरावट है (अस्वीकरण: मैं उस पैकेज का लेखक हूं)। मैपिंग के आदेश को संरक्षित करना 2015 में पहले संस्करण (0.1) में जोड़े गए चीजों में से एक था। न केवल यह आपके शब्दकोशों के आदेश को संरक्षित करता है, यह टिप्पणियों, एंकर के नामों, टैगों को भी संरक्षित करेगा और YAML 1.2 का समर्थन करता है। विनिर्देश (जारी 2009)

विनिर्देश कहता है कि ऑर्डर की गारंटी नहीं है, लेकिन निश्चित रूप से YAML फ़ाइल में ऑर्डरिंग है और उपयुक्त पार्सर बस उस पर पकड़ बना सकता है और पारदर्शी रूप से ऑर्डर रखने वाली एक वस्तु उत्पन्न कर सकता है। आपको बस सही पार्सर, लोडर और डम्पर चुनने की आवश्यकता है:

import sys
from ruamel.yaml import YAML

yaml_str = """\
3: abc
conf:
    10: def
    3: gij     # h is missing
more:
- what
- else
"""

yaml = YAML()
data = yaml.load(yaml_str)
data['conf'][10] = 'klm'
data['conf'][3] = 'jig'
yaml.dump(data, sys.stdout)

तुम्हे दूंगा:

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

dataउस प्रकार का है CommentedMapजो एक तानाशाह की तरह कार्य करता है, लेकिन अतिरिक्त जानकारी है जिसे डंप होने तक रखा जाता है (संरक्षित टिप्पणी सहित!)


यह बहुत अच्छा है अगर आपके पास पहले से ही एक YAML फ़ाइल है, लेकिन आप पायथन संरचना का उपयोग कैसे करते हैं? मैंने CommentedMapसीधे उपयोग करने की कोशिश की लेकिन यह काम नहीं करता है, और हर जगह OrderedDictडालता !!omapहै जो बहुत उपयोगकर्ता के अनुकूल नहीं है।
होल्ट

मुझे यकीन नहीं है कि CommentedMap ने आपके लिए काम क्यों नहीं किया। क्या आप अपने (कम से कम) कोड के साथ एक प्रश्न पोस्ट कर सकते हैं और इसे ruamel.yaml टैग कर सकते हैं? इस तरह मुझे सूचित किया जाएगा और जवाब दिया जाएगा।
एंथन

क्षमा करें, मुझे लगता है कि क्योंकि मैं बचाने की कोशिश की यह CommentedMapसाथ safe=Trueमें YAMLजो काम नहीं किया, (का उपयोग कर safe=Falseकाम करता है)। मेरे पास CommentedMapसंशोधित करने योग्य नहीं होने के साथ भी मुद्दा था , लेकिन मैं इसे अब पुन: पेश नहीं कर सकता ... अगर मैं इस मुद्दे का फिर से सामना करता हूं तो मैं एक नया प्रश्न खोलूंगा।
होल्ट

आपको उपयोग करना चाहिए yaml = YAML(), आपको राउंड-ट्रिप पार्सर / डम्पर मिलता है और यह सुरक्षित पार्सर / डम्पर से व्युत्पन्न होता है, जो कि कमेंटपेड / सीक आदि के बारे में जानता है
एंथन

14

नोट : एक पुस्तकालय है, जो निम्नलिखित उत्तर पर आधारित है, जो 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

यदि आप key_node.start_markअपने त्रुटि संदेश में विशेषता को शामिल करना चाहते हैं , तो मुझे आपके केंद्रीय निर्माण लूप को सरल बनाने का कोई स्पष्ट तरीका नहीं दिख रहा है। यदि आप इस तथ्य का उपयोग करने की कोशिश करते हैं कि OrderedDictनिर्माणकर्ता कुंजी, मूल्य जोड़े के एक चलने योग्य को स्वीकार करेगा, तो आप त्रुटि संदेश उत्पन्न करते समय उस विवरण तक पहुंच खो देते हैं।
ncoghlan

क्या किसी ने इस कोड को ठीक से जांचा है? मैं अपने आवेदन में काम करने के लिए इसे प्राप्त नहीं कर सकता हूँ!
TheAlse

उदाहरण उपयोग: ऑर्डर_डक्ट = yaml.load ('' b: 1 a: 2 '' ', लोडर = ऑर्डरडीडिक्टमलैडलर) # ऑर्डर_डक्ट = ऑर्डरडेड ([(बी', 1), ('ए', 2)]) दुर्भाग्य से पोस्ट को मेरा संपादन अस्वीकार कर दिया गया था, इसलिए कृपया फ़ॉर्मेटिंग की कमी का बहाना करें।
कर्नल पैनिक

यह कार्यान्वयन ऑर्डर किए गए मैपिंग प्रकारों की लोडिंग को तोड़ता है । इसे ठीक करने के लिए, आप add_constructorअपनी __init__विधि में दूसरी कॉल को हटा सकते हैं ।
रयान

10

अद्यतन : लाइब्रेरी को yamlloader के पक्ष में हटा दिया गया था (जो yamlordereddictloader पर आधारित है)

मुझे सिर्फ एक पायथन लाइब्रेरी ( https://pypi.python.org/pypi/yamlordereddictloader/0.1.1 ) मिली है, जो इस प्रश्न के उत्तर के आधार पर बनाई गई थी और उपयोग करने के लिए काफी सरल है:

import yaml
import yamlordereddictloader

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

मैं नहीं जानता कि अगर एक ही लेखक tis है या नहीं, लेकिन yodlgithub पर देखें।
मिस्टर बी

3

Python 2.7 के लिए मेरे PyYaml इंस्टालेशन पर मैंने __init__.py, constructor.py, और loader.py को अपडेट किया। अब लोड कमांड्स के लिए 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)

यह वास्तव में अपस्ट्रीम जोड़ा जाना चाहिए।
माइकल

1
जस्टेड ने अपने परिवर्तनों के साथ एक पुल अनुरोध दायर किया। github.com/yaml/pyyaml/pull/12 मर्ज के लिए आशा करते हैं।
माइकल

वास्तव में इच्छा है कि लेखक अधिक सक्रिय था, आखिरी प्रतिबद्ध 4 साल पहले था। यह बदलाव मेरे लिए एक ईश्वरीय देन होगी।
मार्क लीमोइन

-1

यहां एक सरल समाधान है जो आपके नक्शे में डुप्लिकेट किए गए शीर्ष स्तर की कुंजी के लिए भी जांच करता है।

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])
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.