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
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)
>>> 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
>>>
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).
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.
"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.
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.
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:
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
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))