পাইথনে, আপনি কীভাবে অর্ডারডিক্টস হিসাবে YAML ম্যাপিং লোড করতে পারেন?


128

আমি পাইওয়ামএল এর লোডারকে ম্যাপিংগুলি লোড করার জন্য (এবং ম্যাপিংগুলি অর্ডার দিয়েছি ) পাইথন ২.7+ অর্ডারডিক্ট টাইপের পরিবর্তে ভ্যানিলা dictএবং বর্তমানে যে জোড়গুলির ব্যবহার করছে তার তালিকার পরিবর্তে পেতে চাই ।

এটি করার সর্বোত্তম উপায় কী?

উত্তর:


147

আপডেট: পাইথন ৩.6++- তে পাইপিতে কিছু সময়ের জন্য ব্যবহৃত নতুন ডিক প্রয়োগেরOrderedDict কারণে সম্ভবত আপনার মোটেই প্রয়োজন হবে না (যদিও আপাতত সিপিথন বাস্তবায়ন বিশদ বিবেচনা করা হয়)।

আপডেট: পাইথন ৩.7++ তে ডিক অবজেক্টের সন্নিবেশ-আদেশ সংরক্ষণের প্রকৃতিটি পাইথন ল্যাঙ্গুয়েজ অনুচ্ছেদের অফিসিয়াল অংশ হিসাবে ঘোষণা করা হয়েছে , পাইথন ৩. In-এ নতুন কী রয়েছে তা দেখুন ।

সরলতার জন্য আমি @ জেমসের সমাধানটি পছন্দ করি । তবে এটি ডিফল্ট গ্লোবাল 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
এই বাস্তবায়নটি YAML মার্জ ট্যাগগুলি, বিটিডাব্লু
র্যান্ডি

1
@ র্যান্ডি ধন্যবাদ আমি আগে এই দৃশ্যে দৌড়িনি, তবে এখন আমি এটির পাশাপাশি পরিচালনা করার জন্য একটি সংযোজন করেছি (আশা করি)।
কোল্ডফিক্স

9
অ্যারনেবাবেনহাউজারিহাইড পিআইপিআই যথেষ্ট পরিমাণে প্রবাহিত কিনা তা আমি নিশ্চিত নই, তবে আপনি যদি মনে করেন এটির কাজটি হয় তবে রুমেল.আইএমএল (আমি এর লেখক) দেখুন।
অ্যান্থন

1
আপনার ওয়েবসাইট তার জন্য ধন্যবাদ.
জান 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 তে সমানভাবে কার্যকর হয়
মিডনাইটার

5
এটি PyYAML ( represent_dictএবং DEFAULT_MAPPING_TAG) এর অননুমোদিত বৈশিষ্ট্যগুলি ব্যবহার করছে বলে মনে হচ্ছে । এটি কি কারণ ডকুমেন্টেশন অসম্পূর্ণ, বা এই বৈশিষ্ট্যগুলি অসমর্থিত এবং বিজ্ঞপ্তি ছাড়াই পরিবর্তিত হতে পারে?
aldel

3
মনে রাখবেন এর জন্য dict_constructorআপনাকে কল loader.flatten_mapping(node)করতে হবে অথবা আপনি লোড করতে পারবেন না <<: *...(সিনট্যাক্স মার্জ করে)
অ্যান্টনি সটাইল

@ brice-m-dempsey আপনি কীভাবে আপনার কোডটি ব্যবহার করবেন কোনও উদাহরণ যুক্ত করতে পারেন? মনে হচ্ছে এটি আমার ক্ষেত্রে কাজ করছে না (পাইথন ৩.7)
স্কেফ

53

2018 বিকল্প:

oyamlপাইওয়ামএল -এর একটি ড্রপ-ইন প্রতিস্থাপন যা ডিক্ট অর্ডার সংরক্ষণ করে। পাইথন 2 এবং পাইথন 3 উভয়ই সমর্থিত। ঠিক আছে pip install oyaml, এবং নীচে প্রদর্শিত হিসাবে আমদানি করুন:

import oyaml as yaml

ডাম্পিং / লোড করার সময় আপনি আর স্ক্রু-আপ ম্যাপিংগুলিতে বিরক্ত হবেন না।

দ্রষ্টব্য: আমি oyaml এর লেখক।


1
এই জন্য আপনাকে ধন্যবাদ! কিছু কারণে পাইথন ৩.৮ এর সাথেও পাইওয়ামেলের সাথে অর্ডারটি সম্মান করা হয়নি। oyaml তাৎক্ষণিকভাবে আমার জন্য এটি সমাধান করে।
জন স্মিথ ption

26

2015 (এবং পরবর্তী) বিকল্প:

রুয়ামেল.আইএমএল হল পাইওয়ামএল- এর প্রতিস্থাপনের ড্রপ (দাবি অস্বীকার: আমি সেই প্যাকেজের লেখক)। ২০১৫ সালে ফিরে প্রথম সংস্করণে (০.১) ম্যাপিংয়ের ক্রম সংরক্ষণ করা ছিল one স্পেসিফিকেশন (২০০৯ প্রকাশিত)

স্পেসিফিকেশন বলছে যে অর্ডারিং গ্যারান্টিযুক্ত নয়, তবে অবশ্যই ওয়াইএএমএল ফাইলে অর্ডার রয়েছে এবং উপযুক্ত পার্সার কেবল এটি ধরে রাখতে পারে এবং স্বচ্ছভাবে অর্ডার রাখে এমন একটি বস্তু তৈরি করতে পারে। আপনাকে কেবল সঠিক পার্সার, লোডার এবং ডাম্পার বেছে নিতে হবে:

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

dataCommentedMapডিকের মতো যা কাজ করে তা ধরণের , তবে অতিরিক্ত তথ্য যা ডাম্প হওয়া পর্যন্ত রাখা হয় (সংরক্ষিত মন্তব্য সহ!)


এটি ইতিমধ্যে যদি আপনার কাছে একটি ওয়াইএএমএল ফাইল থাকে তবে এটি খুব সুন্দর তবে আপনি পাইথন কাঠামোটি ব্যবহার করে এটি কীভাবে করবেন? আমি CommentedMapসরাসরি ব্যবহার করার চেষ্টা করেছি কিন্তু এটি কার্যকর হয় না এবং সর্বত্র OrderedDictরাখে !!omapযা খুব ব্যবহারকারী-বান্ধব নয়।
হল্ট

আমি নিশ্চিত না যে কমেন্টেডম্যাপ আপনার পক্ষে কেন কাজ করে নি। আপনি কি আপনার (ন্যূনতম) কোড দিয়ে একটি প্রশ্ন পোস্ট করতে পারেন এবং এটি রমেল.আইএমএল ট্যাগ করতে পারেন? এইভাবে আমাকে অবহিত করা হবে এবং উত্তর দেওয়া হবে।
অ্যান্থন

দুঃখিত, আমি মনে করি, কারণ আমি রক্ষার চেষ্টা এটা CommentedMapদিয়ে safe=Trueমধ্যে YAMLযা কাজ করে নি, (ব্যবহার safe=Falseকাজ)। CommentedMapপরিবর্তনযোগ্য না হওয়ার বিষয়ে আমারও সমস্যা ছিল , তবে আমি এখনই এটি পুনরুত্পাদন করতে পারি না ... আমি যদি আবার এই সমস্যাটির মুখোমুখি হই তবে আমি একটি নতুন প্রশ্ন খুলব।
হল্ট

আপনার ব্যবহার করা উচিত yaml = YAML(), আপনি রাউন্ড-ট্রিপ পার্সার / ডাম্পার পান এবং এটি সেফ পার্সার / ডাম্পার থেকে প্রাপ্ত যে মন্তব্য করা ম্যাপ / সিক ইত্যাদি সম্পর্কে জানে
অ্যান্থন

14

দ্রষ্টব্য : নীচের উত্তরের উপর ভিত্তি করে একটি গ্রন্থাগার রয়েছে, যা ক্লোয়ডার এবং সিডিম্পারগুলি প্রয়োগ করে: ফিনিক্স / ইয়ামলোডার

আমি খুব সন্দেহ করি যে এটি করার সর্বোত্তম উপায় এটি তবে আমি এইভাবেই এগিয়ে এসেছি এবং এটি কাজ করে। গিস্ট হিসাবেও উপলব্ধ ।

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

উদাহরণ ব্যবহার: অর্ডারড_ডিক্ট = ইয়ামল.লোড ('' 'বি: 1 এ: 2' '', লোডার = অর্ডারডটিক্যাল এমএএমএলএলডার) # অর্ডারড_ডিক্ট = অর্ডারডিক্ট ([('বি', 1), ('এ', 2)])) দুর্ভাগ্যক্রমে পোস্টে আমার সম্পাদনা প্রত্যাখ্যান করা হয়েছিল, সুতরাং দয়া করে বিন্যাসের অভাবটি ক্ষমা করুন।
কর্নেল আতঙ্ক

এই প্রয়োগটি আদেশযুক্ত ম্যাপিংয়ের ধরণ লোড করে । এটি ঠিক করতে, আপনি add_constructorনিজের __init__পদ্ধতিতে দ্বিতীয় কলটি সরাতে পারেন ।
রায়ান 23

10

আপডেট : গ্রন্থাগারটি ইয়ামলোএডার (যা ইয়ামলর্ডারডিক্ট্রোডারের উপর ভিত্তি করে) এর পক্ষে অবমুক্ত করা হয়েছিল

আমি এইমাত্র একটি পাইথন গ্রন্থাগার পেয়েছি ( https://pypi.python.org/pypi/yamlordereddictloader/0.1.1 ) যা এই প্রশ্নের উত্তরের ভিত্তিতে তৈরি হয়েছিল এবং এটি ব্যবহার করা বেশ সহজ:

import yaml
import yamlordereddictloader

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

আমি জানি না এটি একই লেখক না কি না, তবে yodlগিথুব দেখুন।
মিঃ বি

3

পাইথন ২.7 এর জন্য পাইওয়ামাল ইনস্টলের জন্য আমি __init__.py, কনস্ট্রাক্টর.পি এবং লোডার.পি আপডেট করেছি। এখন লোড কমান্ডের জন্য অবজেক্ট_পেইস_হুক বিকল্পটি সমর্থন করে। আমার করা বিভিন্ন পরিবর্তনের নীচে রয়েছে।

__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 বছর আগে। এই পরিবর্তনটি আমার কাছে এক seশ্বরের দেবতা হবে।
লেমোইন

-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])
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.