tags:

views:

165

answers:

8

I was just wondering which is the more efficient/faster/better way to check if a key exists.

if 'subject' in request.POST:
    subject = request.POST['subject']
else:
    // handle error

OR

try:
    subject = request.POST['subject']
except KeyError:
    // handle error
A: 
subject = request.POST.get("subject")
if subject is None:
   ...

:)

Messa
And if the value associated with `"subject"` *is* `None`?
Jon-Eric
This is less readable than either of the solutions in the original post. It replaces exceptions with return value checking and is less direct than a containment check, and it has a bug if `request.Post['subject']` already is `None`.
Mike Graham
@Jon-Eric: I presume it can't be because all HTTP posted values are strings.
recursive
+1  A: 

I think it depends on whether 'subject' not being in POST is actually an exception. If it is not supposed to happen but you are just being extra careful, then your second method would I assume be more efficient and quicker. However if you are using the check to do 1 thing or another then it is not appropriate to use an exception. From the look of your code, I would go with your second option.

Toby Allen
It is certainly an exceptional circumstance (even if it occurs 90% of the time!).
Mike Graham
How could something that occurs 90% of the time be exceptional?
Toby Allen
All other cases (key does exist) are in a different category than it.
Mike Graham
thats not an exception thats a condition.
Toby Allen
+2  A: 

The second will fail with collections.defaultdict, and the exception will cause a small performance bump. Other than there there is no real difference between the two.

Ignacio Vazquez-Abrams
It's technically true that the second method will fail with `collections.defaultdict`, but more fundamentally, *this is why* `collections.defaultdict` *exists*.
Mike Graham
+2  A: 

Last time I checked, the first one is a few nanoseconds faster. But most phythonistas seem to favor the second one.

I think I'm not the only one that want to reserve exceptions for exceptional behavior, so I try to use the first one, reserving the second one when it's invalid not to have the key

Javier
This *is* an exceptional circumstance.
Mike Graham
+11  A: 

The latter (try/except) form is generally the better form.

try blocks are very cheap but catching an exception can be more expensive. A containment check on a dict tends to be cheap, but not cheaper than nothing. I suspect there will be a balance of efficiency depending on how often 'subject' is really there. However, this doesn't matter, since premature optimization is useless, distracting, wasteful, and ineffective. You would go with the better solution.

If the code would actually be of the form

if 'subject' in request.POST:
    subject = request.POST['subject']
else:
    subject = some_default

then what you actually want is request.POST.get('subject', some_default).

Mike Graham
+1  A: 

I too like get() you can also specify a default value (other than none) in case that makes sense.

jay
A: 

dict and many dict-like objects (including Django's HttpRequest you seem to be using) allow passing default value to get():

subject = request.POST.get('subject', '[some_default_subject]')

This is preferrable method as it is the shortest and most transparent about your intentions.

Alex Lebedev
A: 

I use .get() method — it is preferable method.

Python 2.5.2 (r252:60911, Jul 22 2009, 15:33:10)
[GCC 4.2.4 (Ubuntu 4.2.4-1ubuntu3)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import dis
>>> def f1(key, d):
...   if key in d:
...     return d[key]
...   else:
...     return "default"
...
>>> dis.dis(f1)
  2           0 LOAD_FAST                0 (key)
              3 LOAD_FAST                1 (d)
              6 COMPARE_OP               6 (in)
              9 JUMP_IF_FALSE           12 (to 24)
             12 POP_TOP

  3          13 LOAD_FAST                1 (d)
             16 LOAD_FAST                0 (key)
             19 BINARY_SUBSCR
             20 RETURN_VALUE
             21 JUMP_FORWARD             5 (to 29)
        >>   24 POP_TOP

  5          25 LOAD_CONST               1 ('default')
             28 RETURN_VALUE
        >>   29 LOAD_CONST               0 (None)
             32 RETURN_VALUE
>>> def f2(key, d):
...   return d.get(key, "default")
...
>>> dis.dis(f2)
  2           0 LOAD_FAST                1 (d)
              3 LOAD_ATTR                0 (get)
              6 LOAD_FAST                0 (key)
              9 LOAD_CONST               1 ('default')
             12 CALL_FUNCTION            2
             15 RETURN_VALUE
>>> def f3(key, d):
...   try:
...     return d[key]
...   except KeyError:
...     return "default"
...
>>> dis.dis(f3)
  2           0 SETUP_EXCEPT            12 (to 15)

  3           3 LOAD_FAST                1 (d)
              6 LOAD_FAST                0 (key)
              9 BINARY_SUBSCR
             10 RETURN_VALUE
             11 POP_BLOCK
             12 JUMP_FORWARD            23 (to 38)

  4     >>   15 DUP_TOP
             16 LOAD_GLOBAL              0 (KeyError)
             19 COMPARE_OP              10 (exception match)
             22 JUMP_IF_FALSE           11 (to 36)
             25 POP_TOP
             26 POP_TOP
             27 POP_TOP
             28 POP_TOP

  5          29 LOAD_CONST               1 ('default')
             32 RETURN_VALUE
             33 JUMP_FORWARD             2 (to 38)
        >>   36 POP_TOP
             37 END_FINALLY
        >>   38 LOAD_CONST               0 (None)
             41 RETURN_VALUE
Aleksa