[Python-ideas] Testing Key-Value Membership In Dictionaries

Steven D'Aprano steve at pearwood.info
Mon Oct 10 03:25:23 CEST 2011


Karthick Sankarachary wrote:
> Hello Python Ideas,
> 
> Currently, to check whether a single key is in a dictionary, we use the "in"
> keyword. However, there is no built-in support for checking if a key-value
> pair belongs in a dictionary.

Of course there is.

(key, value) in d.items()

is explicit and simple. Or if you prefer to avoid an O(N) search through 
a list, the following is only a tiny bit less convenient, but it has the 
advantage of being effectively O(1):

key in d and d[key] == value

Fast, efficient, simple, obvious, explicit and avoids "magic" behaviour.


> Currently, we presuppose that the object being checked has the same type as
> that of the key.

No we don't.

d = {1: 2}
"spam" in d


works perfectly. There's no need to presuppose that the key being tested 
for has the same type as the actual keys in the dict. The only thing we 
suppose is that the given key is hashable. Other than the assumption of 
hashability, there are no assumptions made about the keys.

You want to change that, by assuming that keys aren't tuples.



>  What if we allowed the "in" operator to accept a tuple that
> denotes a (mapped) key-value pair?


Having a single function (or in this case, operator) perform actions 
with different semantics depending on the value of the argument is 
rarely a good idea, especially not for something as basic and 
fundamental as containment tests.

Let me give you an analogy: suppose somebody proposed that `substring in 
string` should do something different depending on whether substring was 
made up of digits or not:

"x" in "a12-50" => returns False, simple substring test
"2" in "a12-50" => returns False, numeric test 2 in the range 12-50?
"20" in "a12-50" => returns True, because 20 is in the range 12-50


And then they propose a hack to avoid the magic test and fall back on 
the standard substring test:

"^20" in "a12-50" => returns False, simple substring test
"^20" in "a12034" => returns True


and a way to escape the magic hack:

"^^20" in "a12034" => returns False
"^^20" in "a1^2034" => returns True

When you understand why this is a terrible idea, you will understand why 
overloading the in operator to magically decide whether you want a key 
test or a key/value test is also a terrible idea. The fact that in your 
proposal, you are checking the class of the argument, but in mine I am 
checking the value of the argument, is irrelevant.


One of the problems with the proposal is that in the event that the 
argument is not a literal, you can't tell what the code will do:

x in some_dict

With your proposal, you simply can't tell: it might test for a key, or 
it might test for a key/value test, and you can't tell which until 
runtime when x has it's value. But normally you want one or the other: 
the two tests have different meanings: you either want to test for a 
key, or for a key/value. I can't think of any case where you don't care 
which you get.

So to write a function that includes a test for a key, you are forced to 
write complicated code with an inconvenient type-check:

def function(d, key):
     # I only want to test for a key
     if ((key,) in d if isinstance(key, tuple) else key in d):
         print("key detected")


To say nothing of the code that you will break -- this is a major 
backwards incompatible change, changing the semantics of existing code. 
Even if it were a good idea, it would be a bad idea.



-- 
Steven



More information about the Python-ideas mailing list