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