Is there a built-in function to say that dict A contains another dict B?
These are "short circuits". When the first element b2
that is not in is found a
, it all()
terminates immediately. Also avoids memory overhead when creating temporary sets
>>> a = { 'name': 'mary', 'age': 56, 'gender': 'female' }
>>> b1 = { 'name': 'mary', 'age': 56 }
>>>
>>> all(a[k]==v for k,v in b1.iteritems())
True
>>> b2 = { 'name': 'elizabeth', 'age': 56 }
>>> all(a[k]==v for k,v in b2.iteritems())
False
In case it b
contains keys that are not in a
, you can use this
>>> all(a.get(k, object())==v for k,v in b2.iteritems())
False
source to share
I'm going to answer if the dictionaries are compatible, so I changed the sample:
>>> test_compat = lambda d1, d2: all(d1[k]==d2[k] for k in set(d1) & set(d2))
>>> a = { 'name': 'mary', 'age': 56, 'gender': 'female' }
>>> b1 = { 'name': 'mary', 'age': 56, 'phone' : '555' }
>>> b2 = { 'name': 'elizabeth', 'age': 56 }
>>> test_compat(a, b1)
True
>>> test_compat(a, b2)
False
>>> test_compat(b1, a)
True
set(d1) & set(d2)
is the intersection of all keys between two dictionaries. all
will be before any mismatch of the corresponding values.
source to share
Yes there is a built-in function. dict.items()
returns a kind of dictionary that behaves the same as set
:
In [1]: a = { 'name': 'mary', 'age': 56, 'gender': 'female' }
...: b1 = { 'name': 'mary', 'age': 56 }
...:
In [2]: b1.items() <= a.items()
Out[2]: True
In [3]: b2 = { 'name': 'elizabeth', 'age': 56 }
In [4]: b2.items() <= a.items()
Out[4]: False
The <
, <=
>=
and operators >
have the same meaning as for set
s.
In python2.7, you can access the views dict
using the method viewitems()
.
This is much better than directly converting the elements to set
(as in the other suggested answers) because:
-
It works with non-writable values:
In [10]: a = {'a': [1]} ...: set(a.items()) <= set(a.items()) --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-10-893acb4047c9> in <module>() 1 a = {'a': [1]} ----> 2 set(a.items()) <= set(a.items()) TypeError: unhashable type: 'list'
While:
In [11]: a.items() <= a.items() Out[11]: True
-
It is more memory efficient because it does not require any other allocations. Usage
set
can double the memory used. - It's faster (since it avoids the overhead of a new allocation).
Simple test with suggested solutions (in python2):
In [1]: a = {'a'+str(i): i for i in range(500000)}
...: b = a.copy()
...:
In [2]: %timeit set(a.iteritems()) <= set(b.iteritems())
1 loops, best of 3: 810 ms per loop
In [3]: %timeit all(a[k]==v for k,v in b.iteritems())
10 loops, best of 3: 157 ms per loop
In [4]: %timeit all(a.get(k,object())==v for k,v in b.iteritems())
1 loops, best of 3: 237 ms per loop
In [5]: %timeit a.viewitems() <= b.viewitems()
10 loops, best of 3: 80.8 ms per loop
In [6]: def test_compat(d1, d2):
...: return all(d1[k]==d2[k] for k in set(d1) & set(d2))
...:
...: def test_compat2(d1, d2):
...: return all(d1[k] == d2[k] for k in d1.viewkeys() & d2.viewkeys())
...:
In [7]: %timeit test_compat(a, b)
1 loops, best of 3: 514 ms per loop
In [8]: %timeit test_compat2(a, b)
1 loops, best of 3: 500 ms per loop
viewitems()
about 2x faster than the second fastest solution.
source to share