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.
--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to