You can use set operations on the dictionary keys view:
if len(kargs.viewkeys() & {'dollar', 'euro'}) != 1:
raise ValueError('One keyword argument is required: dollar=x or euro=x')
In Python 3, use kargs.keys()
instead.
Demo of the different outcomes of the set operation:
>>> kargs = {'dollar': 1, 'euro': 3, 'foo': 'bar'}
>>> kargs.viewkeys() & {'dollar', 'euro'}
set(['dollar', 'euro'])
>>> del kargs['euro']
>>> kargs.viewkeys() & {'dollar', 'euro'}
set(['dollar'])
>>> del kargs['dollar']
>>> kargs.viewkeys() & {'dollar', 'euro'}
set([])
In other words, the &
set intersection gives you a set of all keys present in both sets; both in the dictionary and in your explicit set literal. Only if one and only one of the named keys is present is the length of the intersection going to be 1.
If you do not want to allow any other keyword arguments besides dollar
and euro
, then you can also use proper subset tests. Using <
with two sets is only True if the left-hand set is strictly smaller than the right-hand set; it only has fewer keys than the other set and no extra keys:
if {}.viewkeys() < kargs.viewkeys() < {'dollar', 'euro'}:
raise ValueError('One keyword argument is required: dollar=x or euro=x')
On Python 3, that can be spelled as:
if set() < kargs.keys() < {'dollar', 'euro'}:
instead.
Demo:
>>> kargs = {'dollar': 1, 'euro': 3, 'foo': 'bar'}
>>> {}.viewkeys() < kargs.viewkeys() < {'dollar', 'euro'}
False
>>> del kargs['foo']
>>> {}.viewkeys() < kargs.viewkeys() < {'dollar', 'euro'}
False
>>> del kargs['dollar']
>>> {}.viewkeys() < kargs.viewkeys() < {'dollar', 'euro'}
True
>>> del kargs['euro']
>>> {}.viewkeys() < kargs.viewkeys() < {'dollar', 'euro'}
False
Note that now the 'foo'
key is no longer acceptable.