variables exist

Steven Bethard steven.bethard at gmail.com
Tue Apr 12 17:09:49 EDT 2005


Scott David Daniels wrote:
> Brian van den Broek wrote:
> 
>> ... STeVe stressed that the try/except solution is only really 
>> appropriate for cases where the failure to have the variable defined 
>> is quite rare. 
> 
> Beware: C++ and Java have an immense overhead for exceptions.  Python
> has a very lightweight exception mechanism.  You should _very_seldom_
> choose exceptions or not on the basis of performance without measuring
> the actual use; you are sure to be surprised.

I'll just point out that I wasn't suggesting that try/except should be 
used as an optimization, but that it should be used when *exceptional* 
behavior is encountered.  Using try/except for non-exceptional behavior 
can be confusing for readers of your code who assume the normal 
semantics.  Just to make sure my point is clear, a great case for 
try/except is with dictionaries, e.g.
     try:
         v = d[k]
     except KeyError:
         ...
In this case, the key not being in the dictionary clearly makes sense as 
exceptional behavior because there's no reason to have a dictionary if 
there's nothing in it.  Similarly, try/excepts are great for dealing 
with duck typing issues, e.g.:
     def f(mapping)
         try:
             itervalues = mapping.itervalues
         except AttributeError:
             values = (mapping[k] for k in mapping)
         else:
             values = itervalues()
         ...
Again, the point is that the AttributeError is the *exceptional* 
behavior; f expects a mapping with an itervalues method, and if it 
receives an object that doesn't have one, it has to deal with the 
exceptional case of replacing that method.

Anyway, I hope that clarifies my intentions.  Use try/except when it 
makes sense to talk about something as being *exceptional* behavior.  If 
it doesn't, you should probably use if/else.

STeVe

P.S. That said, there *are* performance differences.  Here's a test that 
shows how try/except can be more costly the more often the except clause 
is reached:

----------------------------------------------------------------------
import timeit

def ifelse(mapping, key):
     if key in mapping:
         return mapping[key]
     else:
         return None

def tryexcept(mapping, key):
     try:
         return mapping[key]
     except KeyError:
         return None

setup_code = """\
from __main__ import %s as func
mapping = %s
keys = %s
"""

test_code = '[func(mapping, key) for key in keys]'

def get_time(func, mapping, keys):
     return timeit.Timer(test_code, setup_code % (
         func.__name__, mapping, keys)).timeit(1000)

if __name__ == '__main__':
     N = 1000
     mapping = dict((i, i**2) for i in xrange(N))
     for i in [0, 1, 2, 4, 8]:
         number_missing = i*N/100
         keys = range(number_missing, N + number_missing)
         ifelse_time, tryexcept_time = (get_time(func, mapping, keys)
                                        for func in [ifelse, tryexcept])
         result = ['%3i%%' % (100*number_missing/N)]
         for time, func in sorted([(ifelse_time, ifelse),
                                   (tryexcept_time, tryexcept)]):
             result.extend(['%.4f' % time, '%10s' % func.__name__])
         print '\t'.join(result)
----------------------------------------------------------------------

And the results I get from running it:

[D:\Steve]$ test.py
   0%    0.8467   tryexcept      0.9415      ifelse
   1%    0.9374   tryexcept      0.9430      ifelse
   2%    0.9499      ifelse      1.0375   tryexcept
   4%    0.9580      ifelse      1.1576   tryexcept
   8%    0.9333      ifelse      1.4187   tryexcept

Thus try/except is better when almost all of the keys can be found in 
the dict, but as soon as even 2% of the keys cannot, if/else is the more 
efficient solution.  This is toy data, so obviously YMMV.  But if you 
use try/except for a very frequent occurrence instead of an exceptional 
one, you may notice a performance hit.



More information about the Python-list mailing list