पायथन: जांचें कि क्या एक शब्दकोश दूसरे बड़े शब्दकोश का सबसेट है


103

मैं पूर्वनिर्धारित फ़िल्टर विधि की एक मनमाना संख्या लेता है लिखने के लिए कोशिश कर रहा हूँ kwargs और है कि उन शामिल एक सूची डेटाबेस की तरह के तत्वों से युक्त एक सूची लौटाती है kwargs

उदाहरण के लिए, मान लीजिए d1 = {'a':'2', 'b':'3'}और d2एक ही चीज़। d1 == d2सच में परिणाम। लेकिन मान लीजिए d2= एक ही चीज अन्य चीजों का एक गुच्छा है। मेरी विधि को यह बताने में सक्षम होना चाहिए कि क्या d1 d2 में है , लेकिन पायथन शब्दकोशों के साथ ऐसा नहीं कर सकता है।

प्रसंग:

मैं एक शब्द वर्ग है, और प्रत्येक वस्तु की तरह गुण है word, definition, part_of_speech, और इतने पर। मैं इन शब्दों की मुख्य सूची पर एक फिल्टर विधि को कॉल करने में सक्षम होना चाहता हूं, जैसे Word.objects.filter(word='jump', part_of_speech='verb-intransitive')। मैं यह पता नहीं लगा सकता कि इन कुंजियों और मूल्यों को एक ही समय में कैसे प्रबंधित किया जाए। लेकिन इससे अन्य लोगों के लिए इस संदर्भ में बड़ी कार्यक्षमता हो सकती है।

जवाबों:


109

आइटम जोड़े में कनवर्ट करें और रोकथाम के लिए जाँच करें।

all(item in superset.items() for item in subset.items())

अनुकूलन को पाठक के लिए एक अभ्यास के रूप में छोड़ दिया जाता है।


18
Dict मूल्यों, hashable हैं viewitems (का प्रयोग करके) तो सबसे Optimizied तरह से मैं के बारे में सोच सकता है: d1.viewitems() <= d2.viewitems()। समय-समय पर रन 3x के प्रदर्शन में सुधार दिखाते हैं। यदि नहीं धो सकते हैं, यहां तक ​​कि 1.2x सुधार के लिए सुराग के iteritems()बजाय का उपयोग कर items()। यह पायथन 2.7 का उपयोग करके किया गया था।
चाड

34
मुझे नहीं लगता कि अनुकूलन को पाठक पर छोड़ दिया जाना चाहिए - मैं चिंतित हूं कि लोग वास्तव में इसका उपयोग किए बिना यह महसूस करेंगे कि यह सुपरसेट.इटीम्स () की एक प्रति बनाने जा रहा है और उपसमुच्चय में प्रत्येक आइटम के लिए इसके माध्यम से पुनरावृत्ति करता है।
रोबर्ट राजा

4
अजगर के साथ 3 items()प्रतियों के बजाय हल्के विचार लौटाएगा। आगे कोई अनुकूलन आवश्यक नहीं है।
केंटोजो

3
नेस्टेड निर्देशिका के बारे में क्या?
एंड्रियास प्रोफेस

5
यह उल्लासपूर्ण है। मैं हास्य के विषय शोधन पाठक को छोड़ दूँगा।
गहरीकरण

97

पायथन 3 में, आप dict.items()तानाशाह वस्तुओं का एक सेट जैसा दृश्य प्राप्त करने के लिए उपयोग कर सकते हैं । आप <=परीक्षण के लिए ऑपरेटर का उपयोग कर सकते हैं यदि एक दृश्य दूसरे का "सबसेट" है:

d1.items() <= d2.items()

पायथन 2.7 में, dict.viewitems()उसी का उपयोग करें :

d1.viewitems() <= d2.viewitems()

पायथन 2.6 और नीचे में आपको एक अलग समाधान की आवश्यकता होगी, जैसे कि उपयोग करना all():

all(key in d2 and d2[key] == d1[key] for key in d1)

1
python3 के लिए यह बन जाता हैd1.items() <= d2.items()
radu.ciorba

कैविएट: यदि आपका प्रोग्राम संभावित रूप से पायथन 2.6 (या उससे भी नीचे) पर उपयोग किया जा सकता है, तो d1.items() <= d2.items()वास्तव में ट्यूल की 2 सूचियों की तुलना कर रहे हैं, विशेष आदेश के बिना, इसलिए अंतिम परिणाम शायद विश्वसनीय नहीं होगा। इस कारण से, मैं @blubberdiblub के उत्तर पर स्विच करता हूं।
रायलूओ

1
d1.items() <= d2.items()अपरिभाषित व्यवहार है। यह आधिकारिक डॉक्स में प्रलेखित नहीं है और, सबसे महत्वपूर्ण, यह परीक्षण नहीं किया गया है: github.com/python/cpython/blob/… इसलिए यह कार्यान्वयन पर निर्भर है।
रोड्रिगो मार्टिंस डी ओलिवेरा

2
@RodrigoMartins यह यहां प्रलेखित है : "सेट-जैसे विचारों के लिए, सार आधार वर्ग के लिए परिभाषित सभी ऑपरेशन collections.abc.Setउपलब्ध हैं"
बरमा

1
@RodrigoMartins यदि आप भविष्य के रखरखाव के बारे में चिंतित हैं, तो ऑपरेशन को स्पष्ट रूप से नामित फ़ंक्शन में लपेटें या कोड टिप्पणी जोड़ें। अपने कोड मानकों को अक्षम डेवलपर्स के स्तर तक कम करना एक भयानक विचार है।
अगुरार

36

उन लोगों के लिए ध्यान दें, जिन्हें इकाई परीक्षण के लिए इसकी आवश्यकता होती है: assertDictContainsSubset()पायथन की TestCaseकक्षा में एक विधि भी है ।

http://docs.python.org/2/library/unittest.html?highlight=assertdictcontainssubset#unittest.TestCase.assertDictContainsSubset

यह 3.2 में चित्रित किया गया है, निश्चित रूप से क्यों, शायद इसके लिए कोई प्रतिस्थापन नहीं है।


29
उत्सुक था, यह 3.2 में नया क्या है में पाया गया : AssertDictContainsSubset () विधि को हटा दिया गया था क्योंकि इसे गलत क्रम में तर्कों के साथ गलत तरीके से लागू किया गया था। इसने हार्ड-टू-डीबग ऑप्टिकल भ्रम पैदा कर दिया, जहां TestCase ()। AssertDictContainsSubset ({'a': 1, 'b': 2}, {'a': 1}) जैसे परीक्षण विफल हो जाएंगे। (रेमंड
हेटिंगर

2
रुको, बाईं ओर की उम्मीद है और दाईं ओर वास्तविक है ... क्या यह विफल नहीं होना चाहिए? फ़ंक्शन के साथ केवल एक चीज गलत है कि कौन सी जगह में जाता है भ्रामक है?
जेम्सहिसन

21

कुंजियों और मूल्यों के लिए चेक का उपयोग करें: set(d1.items()).issubset(set(d2.items()))

यदि आपको केवल कुंजियाँ जाँचने की आवश्यकता है: set(d1).issubset(set(d2))


11
पहली अभिव्यक्ति काम नहीं करेगी यदि किसी भी शब्दकोश में कोई भी मूल्य उपलब्ध नहीं है।
पेड्रो रोमानो

6
दूसरा उदाहरण सेट (d2) को हटाकर थोड़ा छोटा किया जा सकता है, क्योंकि "issubset किसी भी चलने योग्य को स्वीकार करता है"। docs.python.org/2/library/stdtypes.html#set
trojjer

यह गलत है: d1={'a':1,'b':2}; d2={'a':2,'b':1}-> दूसरा स्निपेट वापस आएगा True...
फ्रांसेस्को पासा

1
@FrancescoPasa दूसरा स्निपेट स्पष्ट रूप से कहता है: "यदि आपको केवल कुंजियों की जांच करने की आवश्यकता है"। {'a', 'b'}वास्तव में {'a', 'b'};) का एक सबसेट है ;)
डायलनयुंग

19

पूर्णता के लिए, आप यह भी कर सकते हैं:

def is_subdict(small, big):
    return dict(big, **small) == big

हालाँकि, मैं कोई दावा नहीं करता कि जो भी गति (या उसके अभाव) या पठनीयता (या उसके अभाव) से संबंधित है।


एक ओर ध्यान दें: उल्लेख करने वाले अन्य उत्तर small.viewitems() <= big.viewitems()आशाजनक थे, लेकिन एक चेतावनी के साथ: यदि आपका प्रोग्राम पाइथन 2.6 (या उससे भी नीचे) पर भी इस्तेमाल किया जा सकता है, तो d1.items() <= d2.items()वास्तव में ट्यूल की 2 सूचियों की तुलना कर रहे हैं, विशेष आदेश के बिना, इसलिए अंतिम परिणाम शायद होगा। ग़ैरभरोसेमंद। उस कारण से, मैं @blubberdiblub के उत्तर पर स्विच करता हूं। Upvoted।
रायलूओ

यह अच्छा है, लेकिन यह नेस्टेड शब्दकोशों के साथ काम नहीं करता है।
फ्रेडरिक बेटेंस

@FrederikBaetens इसका मतलब नहीं है। इसके अलावा, मेरा मानना ​​है कि ऐसा करने का कोई आम तौर पर स्वीकृत तरीका नहीं है, क्योंकि ऐसे कई तरीके हैं जिनके बारे में आप जा सकते हैं और ऐसे कई संभावित ढांचे / प्रतिबंध हैं जिन्हें आप ऐसे शब्दकोशों पर लगा सकते हैं। यहाँ कुछ सवाल हैं जो दिमाग में आते हैं: आप यह कैसे निर्धारित करते हैं कि क्या एक गहरे शब्दकोश में उतरना चाहिए? dictबेस क्लास के रूप में एक प्रकार की वस्तुओं के बारे में क्या ? क्या होगा अगर यह अभी भी नहीं है और एक की तरह व्यवहार करता है dict? क्या होगा smallऔर अगर bigएक मिलान कुंजी पर विभिन्न प्रकार के मूल्य होते हैं जो अभी भी तानाशाही की तरह व्यवहार करते हैं?
ब्लबरडाइब्लूब

वे मान्य बिंदु हैं, लेकिन एक मूल फ़ंक्शन जो सादे नेस्टेड डिक्ट्स के साथ काम करता है, वह अच्छा होना चाहिए। मैंने यहां एक उदाहरण पोस्ट किया है , लेकिन @ नटक्रैकर का समाधान बेहतर है
फ्रेडरिक बैटेन्स

निश्चित रूप से, यह नेस्टेड शब्दकोशों के बारे में एक सवाल था (और शब्दकोशों के लिए सटीक आवश्यकताओं को रेखांकित किया गया था), मुझे इसके साथ दरार पड़ सकती थी। मुद्दा यह है कि नेस्टेड शब्दकोशों के लिए एक समाधान सही उत्तर नहीं देता है जब आप यह जानना चाहते हैं कि क्या एक तानाशाह दूसरे तरीके से एक सपाट तरीके से अधीन है (यानी जब आप उत्तर चाहते हैं तो Falseजब सख्ती से उत्तीर्ण होने वाले मानों का जवाब हो। मिलान कुंजी के लिए अलग हैं)। या दूसरे शब्दों में: नेस्टेड डिक्ट्स का समाधान जरूरी नहीं है कि उपयोग के मामले के आधार पर एक ड्रॉप-इन प्रतिस्थापन हो।
blubberdiblub

10
>>> d1 = {'a':'2', 'b':'3'}
>>> d2 = {'a':'2', 'b':'3','c':'4'}
>>> all((k in d2 and d2[k]==v) for k,v in d1.iteritems())
True

संदर्भ:

>>> d1 = {'a':'2', 'b':'3'}
>>> d2 = {'a':'2', 'b':'3','c':'4'}
>>> list(d1.iteritems())
[('a', '2'), ('b', '3')]
>>> [(k,v) for k,v in d1.iteritems()]
[('a', '2'), ('b', '3')]
>>> k,v = ('a','2')
>>> k
'a'
>>> v
'2'
>>> k in d2
True
>>> d2[k]
'2'
>>> k in d2 and d2[k]==v
True
>>> [(k in d2 and d2[k]==v) for k,v in d1.iteritems()]
[True, True]
>>> ((k in d2 and d2[k]==v) for k,v in d1.iteritems())
<generator object <genexpr> at 0x02A9D2B0>
>>> ((k in d2 and d2[k]==v) for k,v in d1.iteritems()).next()
True
>>> all((k in d2 and d2[k]==v) for k,v in d1.iteritems())
True
>>>

4

एक ही उद्देश्य के लिए मेरा कार्य, यह पुनरावर्ती करना:

def dictMatch(patn, real):
    """does real dict match pattern?"""
    try:
        for pkey, pvalue in patn.iteritems():
            if type(pvalue) is dict:
                result = dictMatch(pvalue, real[pkey])
                assert result
            else:
                assert real[pkey] == pvalue
                result = True
    except (AssertionError, KeyError):
        result = False
    return result

आपके उदाहरण में, dictMatch(d1, d2)यह सच होना चाहिए भले ही d2 में अन्य सामान हो, साथ ही यह निम्न स्तर पर भी लागू होता है:

d1 = {'a':'2', 'b':{3: 'iii'}}
d2 = {'a':'2', 'b':{3: 'iii', 4: 'iv'},'c':'4'}

dictMatch(d1, d2)   # True

नोट: और भी बेहतर समाधान हो सकता है जो if type(pvalue) is dictक्लॉज से बचा जाता है और यहां तक ​​कि व्यापक मामलों (जैसे हैश आदि की सूची) पर भी लागू होता है। इसके अलावा यहां पुनरावृत्ति सीमित नहीं है, इसलिए अपने जोखिम पर उपयोग करें। ;)


4

यहां एक समाधान है जो शब्दकोश में निहित सूचियों और सेटों में ठीक से पुनरावृत्ति करता है। आप इसका उपयोग dicts आदि सूचियों के लिए भी कर सकते हैं ...

def is_subset(subset, superset):
    if isinstance(subset, dict):
        return all(key in superset and is_subset(val, superset[key]) for key, val in subset.items())

    if isinstance(subset, list) or isinstance(subset, set):
        return all(any(is_subset(subitem, superitem) for superitem in superset) for subitem in subset)

    # assume that subset is a plain value if none of the above match
    return subset == superset

2

यह प्रतीत होता है कि सीधा मुद्दा मुझे 100% विश्वसनीय समाधान खोजने के लिए अनुसंधान में एक दो घंटे खर्च करता है, इसलिए मैंने दस्तावेज किया कि मैंने इस उत्तर में क्या पाया है।

  1. "पायथोनिक-सहयोगी" बोलना, small_dict <= big_dictसबसे सहज तरीका होगा, लेकिन बहुत बुरा है कि यह काम नहीं करेगा{'a': 1} < {'a': 1, 'b': 2}पायथन 2 में प्रतीत होता है, लेकिन यह विश्वसनीय नहीं है क्योंकि आधिकारिक दस्तावेज़ीकरण स्पष्ट रूप से इसे बाहर कहता है। गो खोज "समानता के अलावा अन्य परिणाम लगातार हल किए जाते हैं, लेकिन अन्यथा परिभाषित नहीं होते हैं।" में इस खंड । उल्लेख नहीं करने के लिए, पायथन 3 में 2 डीकट्स की तुलना करने से टाइपऑयर में अपवाद होता है।

  2. दूसरी सबसे सहज बात small.viewitems() <= big.viewitems()केवल पायथन 2.7 के लिए है, और small.items() <= big.items()पायथन 3 के लिए। लेकिन इसमें एक चेतावनी है: यह संभावित रूप से छोटी गाड़ी है । यदि आपके कार्यक्रम का संभावित रूप से पायथन <= 2.6 पर उपयोग किया जा सकता है, तो यह d1.items() <= d2.items()वास्तव में ट्यूल की 2 सूचियों की तुलना कर रहा है, विशेष आदेश के बिना, इसलिए अंतिम परिणाम अविश्वसनीय होगा और यह आपके कार्यक्रम में एक बुरा बग बन जाता है। मैं पायथन <= 2.6 के लिए एक और कार्यान्वयन लिखने के लिए उत्सुक नहीं हूं, लेकिन मैं अभी भी सहज महसूस नहीं करता हूं कि मेरा कोड एक ज्ञात बग के साथ आता है (भले ही यह असमर्थित प्लेटफॉर्म पर हो)। इसलिए मैंने इस दृष्टिकोण को त्याग दिया।

  3. मैं @blubberdiblub के उत्तर के साथ बस गया (क्रेडिट उसके पास जाता है):

    def is_subdict(small, big): return dict(big, **small) == big

    यह इंगित करने योग्य है कि, यह उत्तर ==dicts के बीच व्यवहार पर निर्भर करता है , जिसे आधिकारिक दस्तावेज़ में स्पष्ट रूप से परिभाषित किया गया है, इसलिए प्रत्येक पायथन संस्करण में काम करना चाहिए । खोज पर जाएँ:

    • "यदि समान (कुंजी, मान) जोड़े हैं, तो केवल और उसके बराबर होने पर। इस पृष्ठ में अंतिम वाक्य है
    • "मैपिंग (तानाशाही के उदाहरण) की तुलना यदि और केवल तभी की जाती है, जब उनके पास समान (कुंजी, मूल्य) जोड़े हों। कुंजियों और तत्वों की समानता तुलनात्मकता को लागू करती है।" में इस पेज

2

यहाँ समस्या के लिए एक सामान्य पुनरावर्ती समाधान दिया गया है:

import traceback
import unittest

def is_subset(superset, subset):
    for key, value in subset.items():
        if key not in superset:
            return False

        if isinstance(value, dict):
            if not is_subset(superset[key], value):
                return False

        elif isinstance(value, str):
            if value not in superset[key]:
                return False

        elif isinstance(value, list):
            if not set(value) <= set(superset[key]):
                return False
        elif isinstance(value, set):
            if not value <= superset[key]:
                return False

        else:
            if not value == superset[key]:
                return False

    return True


class Foo(unittest.TestCase):

    def setUp(self):
        self.dct = {
            'a': 'hello world',
            'b': 12345,
            'c': 1.2345,
            'd': [1, 2, 3, 4, 5],
            'e': {1, 2, 3, 4, 5},
            'f': {
                'a': 'hello world',
                'b': 12345,
                'c': 1.2345,
                'd': [1, 2, 3, 4, 5],
                'e': {1, 2, 3, 4, 5},
                'g': False,
                'h': None
            },
            'g': False,
            'h': None,
            'question': 'mcve',
            'metadata': {}
        }

    def tearDown(self):
        pass

    def check_true(self, superset, subset):
        return self.assertEqual(is_subset(superset, subset), True)

    def check_false(self, superset, subset):
        return self.assertEqual(is_subset(superset, subset), False)

    def test_simple_cases(self):
        self.check_true(self.dct, {'a': 'hello world'})
        self.check_true(self.dct, {'b': 12345})
        self.check_true(self.dct, {'c': 1.2345})
        self.check_true(self.dct, {'d': [1, 2, 3, 4, 5]})
        self.check_true(self.dct, {'e': {1, 2, 3, 4, 5}})
        self.check_true(self.dct, {'f': {
            'a': 'hello world',
            'b': 12345,
            'c': 1.2345,
            'd': [1, 2, 3, 4, 5],
            'e': {1, 2, 3, 4, 5},
        }})
        self.check_true(self.dct, {'g': False})
        self.check_true(self.dct, {'h': None})

    def test_tricky_cases(self):
        self.check_true(self.dct, {'a': 'hello'})
        self.check_true(self.dct, {'d': [1, 2, 3]})
        self.check_true(self.dct, {'e': {3, 4}})
        self.check_true(self.dct, {'f': {
            'a': 'hello world',
            'h': None
        }})
        self.check_false(
            self.dct, {'question': 'mcve', 'metadata': {'author': 'BPL'}})
        self.check_true(
            self.dct, {'question': 'mcve', 'metadata': {}})
        self.check_false(
            self.dct, {'question1': 'mcve', 'metadata': {}})

if __name__ == "__main__":
    unittest.main()

नोट: मूल कोड कुछ मामलों में विफल हो जाएगा, फिक्सिंग के लिए क्रेडिट @ ओलिवर-मेलानकोन को जाता है


कोड एक सुपरसेट के साथ विफल हो जाता है जिसमें एक सूची के अंदर एक if not set(value) <= set(superset[key])
तानाशाही होती है

2

अगर आप का उपयोग करने में कोई आपत्ति नहीं pydash है, is_matchतो ऐसा ही है:

import pydash

a = {1:2, 3:4, 5:{6:7}}
b = {3:4.0, 5:{6:8}}
c = {3:4.0, 5:{6:7}}

pydash.predicates.is_match(a, b) # False
pydash.predicates.is_match(a, c) # True

1

मुझे पता है कि यह सवाल पुराना है, लेकिन यहां यह जांचने का मेरा समाधान है कि क्या एक नेस्टेड डिक्शनरी दूसरे नेस्टेड डिक्शनरी का हिस्सा है। समाधान पुनरावर्ती है।

def compare_dicts(a, b):
    for key, value in a.items():
        if key in b:
            if isinstance(a[key], dict):
                if not compare_dicts(a[key], b[key]):
                    return False
            elif value != b[key]:
                return False
        else:
            return False
    return True

0

यह फ़ंक्शन गैर-धोने योग्य मूल्यों के लिए काम करता है। मुझे यह भी लगता है कि यह स्पष्ट और पढ़ने में आसान है।

def isSubDict(subDict,dictionary):
    for key in subDict.keys():
        if (not key in dictionary) or (not subDict[key] == dictionary[key]):
            return False
    return True

In [126]: isSubDict({1:2},{3:4})
Out[126]: False

In [127]: isSubDict({1:2},{1:2,3:4})
Out[127]: True

In [128]: isSubDict({1:{2:3}},{1:{2:3},3:4})
Out[128]: True

In [129]: isSubDict({1:{2:3}},{1:{2:4},3:4})
Out[129]: False

0

एक संक्षिप्त पुनरावर्ती कार्यान्वयन जो नेस्टेड शब्दकोशों के लिए काम करता है:

def compare_dicts(a,b):
    if not a: return True
    if isinstance(a, dict):
        key, val = a.popitem()
        return isinstance(b, dict) and key in b and compare_dicts(val, b.pop(key)) and compare_dicts(a, b)
    return a == b

यह ए और बी डाइक का उपभोग करेगा। यदि किसी को इससे बचने का एक अच्छा तरीका पता है कि अन्य उत्तरों की तरह आंशिक रूप से पुनरावृत्त समाधानों का सहारा लिए बिना, तो कृपया मुझे बताएं। मुझे एक कुंजी के आधार पर सिर और पूंछ में एक हुक को विभाजित करने के तरीके की आवश्यकता होगी।

यह कोड एक प्रोग्रामिंग एक्सरसाइज के रूप में अधिक उपयोगी है, और शायद यहाँ के अन्य समाधानों की तुलना में बहुत धीमा है जो पुनरावृत्ति और पुनरावृत्ति को मिलाता है। @ न्यूट्रेकर का समाधान नेस्टेड शब्दकोशों के लिए बहुत अच्छा है।


1
कोड में कुछ कमी है। यह a(और बाद के किसी भी पहले मूल्य) में मिलने वाले पहले मूल्य को नीचे लाता popitemहै। इसे उसी स्तर पर अन्य वस्तुओं की भी जांच करनी चाहिए। मुझे नेस्टेड डाइक के जोड़े मिले हैं जहां यह गलत उत्तर देता है। (यहाँ एक भविष्य-प्रूफ उदाहरण प्रस्तुत करना कठिन है, क्योंकि यह उसी के आदेश पर निर्भर करता है popitem)
ब्लबरडाइब्लूब

धन्यवाद, अब तय किया जाना चाहिए :)
फ्रेडरिक बेटेंस

0

इस रैपर ऑब्जेक्ट का उपयोग करें जो आंशिक तुलना और अच्छा अंतर प्रदान करता है:


class DictMatch(dict):
    """ Partial match of a dictionary to another one """
    def __eq__(self, other: dict):
        assert isinstance(other, dict)
        return all(other[name] == value for name, value in self.items())

actual_name = {'praenomen': 'Gaius', 'nomen': 'Julius', 'cognomen': 'Caesar'}
expected_name = DictMatch({'praenomen': 'Gaius'})  # partial match
assert expected_name == actual_name  # True
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.