2009-10-29

Exceptions in Python sind langsamer als gedacht

Heute habe ich mal wieder ein Python Aha Erlebnis gehabt. Beim Optimieren einer Klasse habe ich ein paar Benchmarks zum Theme dict() Zugriff und Exception Handling durchgeführt.

Kurz gesagt, brauche ich eine Funktion, die ein Key in mehreren dicts sucht.


Klassisch in Python implementiert man das so:

def get_from_dict_list(dict_list,k):
    for d in dict_list:
        try:
            return d[k]
        except KeyError:
            pass
    return None
Eine alternative Implementation is

def get_from_dict_list(dict_list,k):
    for d in dict_list:
        if k in d:
            return d[k]
    return None
Die erste Methode wird normalweise bevorzugt. Dies hat zwei Gründe: Zum einen gilt der Spruch: "it's easier to ask forgiveness than permission" und exception handling ist ein integraler Bestandteil der Sprache. Zum anderen hat die zweite Methode potentielle Race-Conditions und kann eine unerwartete Exception werfen wenn k aus d gelöscht wird, während die Methode abgearbeitet wird.

Für den Fall, das der Key gleich im ersten dict gefunden wird, ist die erste Methode sogar leicht schneller. Falls aber das Exception Handling getirggert wird, wird es richtig langsam. Auf meinem Laptop liefert timeit einen Faktor von 10(!) zwischen den beiden Methoden, zugunsten der zweiten. Das Exception abfangen und ignorieren kostet auf meinem Laptop 4 Mikrosekunden