मैं PyYAML के लोडर को मैपिंग लोड करने के लिए लोड करना चाहता हूं (और मैपिंग का आदेश दिया) Python 2.7+ ऑर्डर किए गए प्रकार में, वेनिला dict
और वर्तमान में उपयोग किए जाने वाले जोड़ों की सूची के बजाय ।
ऐसा करने का सबसे अच्छा तरीका क्या है?
मैं PyYAML के लोडर को मैपिंग लोड करने के लिए लोड करना चाहता हूं (और मैपिंग का आदेश दिया) Python 2.7+ ऑर्डर किए गए प्रकार में, वेनिला dict
और वर्तमान में उपयोग किए जाने वाले जोड़ों की सूची के बजाय ।
ऐसा करने का सबसे अच्छा तरीका क्या है?
जवाबों:
अपडेट: अजगर 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)
यमल मॉड्यूल आपको प्रक्रिया को उलटने के लिए पायथन ऑब्जेक्ट्स को टेक्स्ट और 'कंस्ट्रक्टर्स' में बदलने के लिए कस्टम 'अभ्यावेदन' निर्दिष्ट करने की अनुमति देता है।
_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)
from six import iteritems
और फिर इसे बदल दें iteritems(data)
ताकि यह पायथन 2 और 3 में समान रूप से अच्छी तरह से काम करे
represent_dict
और DEFAULT_MAPPING_TAG
) की अनिर्दिष्ट सुविधाओं का उपयोग किया जा रहा है । क्या यह इसलिए है क्योंकि दस्तावेज़ अपूर्ण है, या ये सुविधाएँ असमर्थित हैं और बिना सूचना के बदले जा सकती हैं?
dict_constructor
आपको कॉल करने की आवश्यकता loader.flatten_mapping(node)
होगी या आप लोड नहीं कर पाएंगे <<: *...
(सिंटैक्स को मर्ज करना)
oyaml
PyYAML के लिए एक ड्रॉप-इन रिप्लेसमेंट है जो तानाशाही आदेश को संरक्षित करता है। पायथन 2 और पायथन 3 दोनों समर्थित हैं। बस pip install oyaml
, और नीचे दिखाए अनुसार आयात करें:
import oyaml as yaml
डंपिंग / लोडिंग के दौरान आप खराब-अप मैपिंग से परेशान नहीं होंगे।
नोट: मैं oyaml का लेखक हूं।
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
जो एक तानाशाह की तरह कार्य करता है, लेकिन अतिरिक्त जानकारी है जिसे डंप होने तक रखा जाता है (संरक्षित टिप्पणी सहित!)
CommentedMap
सीधे उपयोग करने की कोशिश की लेकिन यह काम नहीं करता है, और हर जगह OrderedDict
डालता !!omap
है जो बहुत उपयोगकर्ता के अनुकूल नहीं है।
CommentedMap
साथ safe=True
में YAML
जो काम नहीं किया, (का उपयोग कर safe=False
काम करता है)। मेरे पास CommentedMap
संशोधित करने योग्य नहीं होने के साथ भी मुद्दा था , लेकिन मैं इसे अब पुन: पेश नहीं कर सकता ... अगर मैं इस मुद्दे का फिर से सामना करता हूं तो मैं एक नया प्रश्न खोलूंगा।
yaml = YAML()
, आपको राउंड-ट्रिप पार्सर / डम्पर मिलता है और यह सुरक्षित पार्सर / डम्पर से व्युत्पन्न होता है, जो कि कमेंटपेड / सीक आदि के बारे में जानता है
नोट : एक पुस्तकालय है, जो निम्नलिखित उत्तर पर आधारित है, जो 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
निर्माणकर्ता कुंजी, मूल्य जोड़े के एक चलने योग्य को स्वीकार करेगा, तो आप त्रुटि संदेश उत्पन्न करते समय उस विवरण तक पहुंच खो देते हैं।
add_constructor
अपनी __init__
विधि में दूसरी कॉल को हटा सकते हैं ।
अद्यतन : लाइब्रेरी को yamlloader के पक्ष में हटा दिया गया था (जो yamlordereddictloader पर आधारित है)
मुझे सिर्फ एक पायथन लाइब्रेरी ( https://pypi.python.org/pypi/yamlordereddictloader/0.1.1 ) मिली है, जो इस प्रश्न के उत्तर के आधार पर बनाई गई थी और उपयोग करने के लिए काफी सरल है:
import yaml
import yamlordereddictloader
datas = yaml.load(open('myfile.yml'), Loader=yamlordereddictloader.Loader)
yodl
github पर देखें।
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)
यहां एक सरल समाधान है जो आपके नक्शे में डुप्लिकेट किए गए शीर्ष स्तर की कुंजी के लिए भी जांच करता है।
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])