Unexpected comparisons in dict lookup

Steven D'Aprano steve+comp.lang.python at pearwood.info
Tue Mar 18 10:20:07 EDT 2014


I stumbled across this unexpected behaviour with Python 2.7 and 3.3. When 
you look up a key in a dict, the key is sometimes compared against other 
keys twice instead of just once.

First, a class that reports when it is being tested for equality, with a 
fixed hash so that we get collisions inside the dict:

class Spam:
    def __init__(self, label):
        self.label = label
    def __str__(self):
        return self.label
    def __hash__(self):
        return 100
    def __eq__(self, other):
        print("comparing %s with %s" % (self, other))
        return self is other


Create a dict and prepare for collisions:

x = Spam("x")
y = Spam("y")
d = {100: 1}  # hash(100) == 100


When we add x to the dict, it collides with the existing key 100, so I 
expect one call to Spam.__eq__, and that's exactly what I get:

py> d[x] = 200
comparing x with 100


But when I try adding y to the dict, it collides with 100, then it 
collides with x, then it apparently collides with x a second time:

py> d[y] = 300
comparing y with 100
comparing x with y
comparing x with y


I expected only two calls to __eq__, not three: first comparing with 100, 
then comparing with x. Checking for keys gives the same result:

py> x in d
comparing x with 100
True
py> y in d
comparing y with 100
comparing x with y
comparing x with y
True


What's more, checking for a key which is not present also compares three 
times instead of twice:

py> Spam("z") in d
comparing z with 100
comparing x with z
comparing x with z
comparing y with z
False


I expected it to compare z with 100, then x *once*, then y, then return 
False.

Why is the dict lookup comparing against x twice? It doesn't seem to be 
the fault of x. If I create a slightly different dict, with the 
collisions in a different order:

py> e = {x: 100}
py> e[100] = 200
comparing x with 100
py> e[y] = 300
comparing x with y
comparing y with 100
comparing y with 100



-- 
Steven



More information about the Python-list mailing list