Using 'in' with a Dict

C

cpmcdaniel

I was wondering if the following two "if" statements compile down to
the same bytecode for a standard Dictionary type:

m = {"foo": 1, "blah": 2}

if "foo" in m:
print "sweet"

if m.has_key("foo"):
print "dude"
 
K

kowboy

I posted too quickly. A little performance testing told me that has_key
is somewhat slower than "in". I used a large number of string keys in
my test.
 
K

Kartic

This is what I did ....

The code generated with the has_key() version is slightly large (3
bytes) than the one with the membership test. The co_varnames for the
two code objects vary, as the second one has the has_key method also,
which the other version does not.

Thanks,
-Kartic
 
F

Fredrik Lundh

I was wondering if the following two "if" statements compile down to
the same bytecode for a standard Dictionary type:

m = {"foo": 1, "blah": 2}

if "foo" in m:
print "sweet"

if m.has_key("foo"):
print "dude"
nope.
1 0 LOAD_CONST 0 ('foo')
3 LOAD_NAME 0 (dict)
6 COMPARE_OP 6 (in)
9 POP_TOP
10 LOAD_CONST 1 (None)
13 RETURN_VALUE 1 0 LOAD_NAME 0 (dict)
3 LOAD_ATTR 1 (has_key)
6 LOAD_CONST 0 ('foo')
9 CALL_FUNCTION 1
12 POP_TOP
13 LOAD_CONST 1 (None)
16 RETURN_VALUE

"in" is a built-in operator, "has_key" is an ordinary method. both
paths end up in the same C function, but the method path is usually
a little bit slower.

</F>
 
S

Steven Bethard

I was wondering if the following two "if" statements compile down to
the same bytecode for a standard Dictionary type:

m = {"foo": 1, "blah": 2}

if "foo" in m:
print "sweet"

if m.has_key("foo"):
print "dude"

To answer the question you actually asked, you can use dis.dis:

py> def f1(s, m):
.... if s in m:
.... pass
....
py> def f2(s, m):
.... if m.has_key(s):
.... pass
....
py> import dis
py> dis.dis(f1)
2 0 LOAD_FAST 0 (s)
3 LOAD_FAST 1 (m)
6 COMPARE_OP 6 (in)
9 JUMP_IF_FALSE 4 (to 16)
12 POP_TOP

3 13 JUMP_FORWARD 1 (to 17) 20 RETURN_VALUE
py> dis.dis(f2)
2 0 LOAD_FAST 1 (m)
3 LOAD_ATTR 1 (has_key)
6 LOAD_FAST 0 (s)
9 CALL_FUNCTION 1
12 JUMP_IF_FALSE 4 (to 19)
15 POP_TOP

3 16 JUMP_FORWARD 1 (to 20) 23 RETURN_VALUE

Note that in the *bytecode*, f1 uses COMPARE_OP, while f2 uses LOAD_ATTR
and CALL_FUNCTION. So no, the bytecode is different.

If the question you meant to as was "Do Python's builtin dicts use the
same code for 'in' and 'has_key'?", you can check dictobject.c:

static PyMethodDef mapp_methods[] = {
{"__contains__",(PyCFunction)dict_has_key, METH_O | METH_COEXIST,
contains__doc__},
...
{"has_key", (PyCFunction)dict_has_key, METH_O,
has_key__doc__},
...
};

Note that both __contains__ and has_key are mapped to dict_has_key. So
yes, the same C code will be executed in both cases[1].

STeVe

[1] modulo the different lookup paths to find these methods
 
S

Steven Bethard

kowboy said:
I posted too quickly. A little performance testing told me that has_key
is somewhat slower than "in". I used a large number of string keys in
my test.

See my other post, but the reason has_key is slower is almost certainly
that it has to do a LOAD_ATTR:

$ python -m timeit -s "m = dict.fromkeys(xrange(100, 200))" "[i in m for
i in xrange(300)]"
10000 loops, best of 3: 102 usec per loop

$ python -m timeit -s "m = dict.fromkeys(xrange(100, 200));
has_key=m.has_key" "[has_key(i) for i in xrange(300)]"
10000 loops, best of 3: 107 usec per loop

For this data at least, the difference is negligible. (I actually found
has_key to be faster in some other datasets.)

STeVe
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,219
Messages
2,571,120
Members
47,741
Latest member
WilliamsFo

Latest Threads

Top