slow try statements in python?

Duncan Booth duncan at NOSPAMrcp.co.uk
Wed Feb 19 09:16:47 EST 2003


Andrew MacIntyre <andymac at bullseye.apana.org.au> wrote in 
news:mailman.1045657194.10823.python-list at python.org:

>> C++ implementations tend to be heavily optimised towards
>> the case where no exceptions are raised, on the assumption
>> that exceptions truly are "exceptional". But there is no
>> such bias in Python -- you're meant to be able to use
>> exceptions freely.
> 
> Notwithstanding this, I think you'll find that it is recognised in the
> collective wisdom that try/except can be noticeably more expensive in
> circumstances where an operation is repeatedly tried with a significant
> likelihood of an exception, compared to other techniques.
> 
> It is not so much the "try" that is expensive, but rather the "except:"
> (ie actually handling the raised exception).
> 
> In general though, as Greg notes, the difference is not worth worrying
> about until you can define a requirement to optimise around it.

In cases like this it can be worth getting a rough idea of the sort of 
times we are talking about. The code below tries accessing a key in a 
dictionary in 3 different ways.

Handling the exception takes 0.3 seconds if the key is found, 2.3 seconds 
if the exception is thrown. So it appears my PC can throw and catch about 
50,000 exceptions per second.

The second method takes 0.81 seconds whether or not the key is found. The 
third method takes 0.48 seconds either way.

So for dictionary lookup, it would seem that if I expect more than 1 miss 
in 10, the get method might be faster than handling the exception, but for 
less than this the exception handler might be faster.

Of course the results will be wildly different for any other situation, so 
perhaps the only useful conclusion is that it is better not to worry about 
speed at all until you find the code is too slow.

------ time.py -------
def testException(aDict):
    try:
        a = aDict["xyz"]
    except KeyError:
        a = "Not found"
    return a

def testHasAttr(aDict):
    if hasattr(aDict, "xyz"):
        a = aDict["xyz"]
    else:
        a = "Not found"
    return a

def testGet(aDict):
    a = aDict.get("xyz", "Not found")
    return a

def timeit(repeat, fn, *args, **kw):
    import time
    start = time.time()
    for i in xrange(repeat):
        fn(*args, **kw)
    end = time.time()
    # Format arguments for printout
    argstring = ', '.join(
        [str(a) for a in args] +
        [ '%s=%s' % (k, v) for k, v in kw.items()])
    if len(argstring) > 60:
        argstring = argstring[:57] + '...'
    print "%.2fs: %d x %s(%s)" % ((end-start), repeat, fn.__name__, 
argstring)

goodDict = { 'abc': 'First', 'def': 'Second', 'xyz': 'Here it is' }
badDict = { 'abc': 'First', 'def': 'Second' }

timeit(100000, testException, goodDict)
timeit(100000, testException, badDict)
timeit(100000, testHasAttr, goodDict)
timeit(100000, testHasAttr, badDict)
timeit(100000, testGet, goodDict)
timeit(100000, testGet, badDict)
------ end time.py ---

-- 
Duncan Booth                                             duncan at rcp.co.uk
int month(char *p){return(124864/((p[0]+p[1]-p[2]&0x1f)+1)%12)["\5\x8\3"
"\6\7\xb\1\x9\xa\2\0\4"];} // Who said my code was obscure?




More information about the Python-list mailing list