How to check if one dictionary is a subset of another larger dictionary?

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 those kwargs.

For example, suppose d1 = {'a':'2', 'b':'3'} and d2 = the same thing. d1 == d2 results in True. But suppose d2 = the same thing plus a bunch of other things. My method needs to be able to tell if d1 in d2, but Python can't do that with dictionaries.

Context:

I have a Word class, and each object has properties like word, definition, part_of_speech, and so on. I want to be able to call a filter method on the main list of these words, like Word.objects.filter(word='jump', part_of_speech='verb-intransitive'). I can't figure out how to manage these keys and values at the same time. But this could have larger functionality outside this context for other people.


Solution 1:

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)

Solution 2:

Convert to item pairs and check for containment.

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

Optimization is left as an exercise for the reader.

Solution 3:

Note for people that need this for unit testing: there's also an assertDictContainsSubset() method in Python's TestCase class.

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

It's however deprecated in 3.2, not sure why, maybe there's a replacement for it.

Solution 4:

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

Update: As pointed out by Boris' comment, this trick does not work if your small dict has non-string keys and you're using Python >= 3 (or in other words: in the face of arbitrarily typed keys, it only works in legacy Python 2.x).

If you are using Python 3.9 or newer, though, you can make it work both with non-string typed keys as well as get an even neater syntax.

Provided your code already has both dictionaries as variables, it's very concise to check for this inline:

if big | small == big:
    # do something

Otherwise, or if you prefer a reusable function as above, you can use this:

def is_subdict(small, big):
    return big | small == big

The working principle is the same as the first function, only this time around making use of the union operator that was extended to support dicts.