I've taken over someone else's code (yes, honestly!) that has a complex class hierarchy on top of the main procedural code. This is unfortunate because it means that isinstance() is everywhere.
Profiling recently highlighted one particular formatted output function that has a cascade of isinstance() tests where the order of the tests is significant, as in the example: def oldOutput(x): if x is None: pass elif isinstance(x, Z): pass elif isinstance(x, Y): pass elif isinstance(x, X): pass elif isinstance(x, int): pass elif isinstance(x, float): pass elif isinstance(x, str): pass else: pass # NOTE: # In the real code, there are various enumeration classes # derived from int, so we can't even test for the built in # types before we test for particular classes. I don't like this, because we are usurping Pythons class handling, and suggested that we create methods in the classes and let Python do the work, and replace the above with something like: def newOutput(x): if x is None: pass return try: x.output() except AttributeError: if isinstance(x, int): pass elif isinstance(x, float): pass elif isinstance(x, str): pass else: pass However, when I verified this example using timeit, the results were completely unexpected. The time to resolve the objects remains the same, but for the built-in types raising and catching the exception means that resolution of built-in types takes 3 or 4 times longer. The improved robustness of the code for objects is obviously good, but not at the expense of killing performance for the built-in types. Have I made a basic boo-boo in my test code? Is there a better way of speeding up the original function? I don't really want to spend hours (days?) implementing this in the real code if I'm barking up the wrong tree. I attach the full example code below. Cheers Duncan #--------------------------------------------------------------------- class X(object): def __init__(self): self.x = 0 def output(self): pass class Y(X): def __init__(self): X.__init__(self) self.y = 0 def output(self): pass class Z(Y): def __init__(self): Y.__init__(self) self.z = 0 def output(self): pass def oldOutput(x): if x is None: pass elif isinstance(x, Z): pass elif isinstance(x, Y): pass elif isinstance(x, X): pass elif isinstance(x, int): pass elif isinstance(x, float): pass elif isinstance(x, str): pass else: pass def newOutput(x): if x is None: pass return try: x.output() except AttributeError: if isinstance(x, int): pass elif isinstance(x, float): pass elif isinstance(x, str): pass else: pass if __name__ == '__main__': from timeit import Timer # first test that the functions 'work' before timing them # for i in (None, 1, 1.0, "one", X(), Y(), Z(), []): oldOutput(i) newOutput(i) # now time the functions # for i in ('None', '1', '1.0', '"one"', 'X()', 'Y()', 'Z()', '[]'): s = 'oldOutput(%s)' % i t = Timer(s, 'from __main__ import X, Y, Z, oldOutput, newOutput') print 'old', i, t.timeit() s = 'newOutput(%s)' % i t = Timer(s, 'from __main__ import X, Y, Z, oldOutput, newOutput') print 'new', i, t.timeit() print _______________________________________________ Tutor maillist - Tutor@python.org http://mail.python.org/mailman/listinfo/tutor