Python: Check if one dictionary is a subset of another larger dictionary

前端 未结 16 642
轮回少年
轮回少年 2020-12-04 09:29

I\'m trying to write a custom filter method that takes an arbitrary number of kwargs and returns a list containing the elements of a database-like list that contain

相关标签:
16条回答
  • 2020-12-04 10:08

    In Python 3, you can use dict.items() to get a set-like view of the dict items. You can then use the <= operator to test if one view is a "subset" of the other:

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

    In Python 2.7, use the dict.viewitems() to do the same:

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

    In Python 2.6 and below you will need a different solution, such as using all():

    all(key in d2 and d2[key] == d1[key] for key in d1)
    
    0 讨论(0)
  • 2020-12-04 10:08
    >>> 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
    

    context:

    >>> 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
    >>>
    
    0 讨论(0)
  • 2020-12-04 10:09

    For completeness, you can also do this:

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

    However, I make no claims whatsoever concerning speed (or lack thereof) or readability (or lack thereof).

    0 讨论(0)
  • 2020-12-04 10:09

    This seemingly straightforward issue costs me a couple hours in research to find a 100% reliable solution, so I documented what I've found in this answer.

    1. "Pythonic-ally" speaking, small_dict <= big_dict would be the most intuitive way, but too bad that it won't work. {'a': 1} < {'a': 1, 'b': 2} seemingly works in Python 2, but it is not reliable because the official documention explicitly calls it out. Go search "Outcomes other than equality are resolved consistently, but are not otherwise defined." in this section. Not to mention, comparing 2 dicts in Python 3 results in a TypeError exception.

    2. The second most-intuitive thing is small.viewitems() <= big.viewitems() for Python 2.7 only, and small.items() <= big.items() for Python 3. But there is one caveat: it is potentially buggy. If your program could potentially be used on Python <=2.6, its d1.items() <= d2.items() are actually comparing 2 lists of tuples, without particular order, so the final result will be unreliable and it becomes a nasty bug in your program. I am not keen to write yet another implementation for Python<=2.6, but I still don't feel comfortable that my code comes with a known bug (even if it is on an unsupported platform). So I abandon this approach.

    3. I settle down with @blubberdiblub 's answer (Credit goes to him):

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

      It is worth pointing out that, this answer relies on the == behavior between dicts, which is clearly defined in official document, hence should work in every Python version. Go search:

      • "Dictionaries compare equal if and only if they have the same (key, value) pairs." is the last sentence in this page
      • "Mappings (instances of dict) compare equal if and only if they have equal (key, value) pairs. Equality comparison of the keys and elements enforces reflexivity." in this page
    0 讨论(0)
  • 2020-12-04 10:11

    If you don't mind using pydash there is is_match there which does exactly that:

    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
    
    0 讨论(0)
  • 2020-12-04 10:12

    for keys and values check use: set(d1.items()).issubset(set(d2.items()))

    if you need to check only keys: set(d1).issubset(set(d2))

    0 讨论(0)
提交回复
热议问题