Callback-Funktionen machen Sinn, wenn Daten in einer Schleife verarbeitet werden und sind eine hervorragende Methode um generische, d.h. wiederverwendbare Klassen zu schreiben. Individueller Code wird dann elegant in Callbacks ausgelagert.
Python Callbacks: Ein einfaches Beispiel
Die folgende Klasse liest eine CSV-Datei und gibt diverse Adressdaten im Terminal aus:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import csv class CSVProcessor: def run(self, f): with open(f, 'r') as file: reader = csv.DictReader(file) for row in reader: print(row['Name'], row['Vorname'], row['Email']) if __name__ == '__main__': p = CSVProcessor() p.run('file.csv') |
Inhalt von file.csv:
1 2 |
"Vorname","Name","Email" "Harald","Schneider","h_schneider@domain.com" |
Problem: Die Klasse kann leider nur für Adressdaten verwendet werden, da die Felder Name, Vorname, EMail individuell sind.
Für Produkt-Daten müsste dann eine extra Klasse geschrieben werden.
Mit wenigen Änderungen kann daraus eine generische Klasse gemacht werden:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
import csv class CSVProcessor: def run(self, f, callback): with open(f, 'r') as file: reader = csv.DictReader(file) for row in reader: callback(row) if __name__ == '__main__': def printFields(row): print(row['Name'], row['Vorname'], row['Email']) p = CSVProcessor() p.run('file.csv', printFields) |
Integration der Callback-Funktion: Die Anpassungen im Detail
- Die Methode run() erhält als zusätzlichen Parameter die Adresse unserer Callback-Funktion.
Der Parameter heisst in unserem Fall callback. Der Name könnte auch beliebig anders gewählt werden. - Aus der Adresse der Callback-Funktion wird wieder ein Funktionsaufruf, indem wir die Klammern samt Parameter hinzufügen:
callback(row). - In __main__ definieren wir unsere Callback-Funktion namens printFields().
- Die Adresse von printFields(), d.h. der Funktionsname ohne Klammern, übergeben wird dann in der letzten Zeile an die run-Methode der Klasse:
p.run(‘products.csv’, printFields)
Für Produkt-Daten schaut der Aufruf dann wie folgt aus:
1 2 3 4 5 |
def printFields(row): print(row['SKU'], row['Produkt'], row['Preis']) p = CSVProcessor() p.run('products.csv', printFields) |
Inhalt von products.csv:
1 2 |
"SKU","Produkt","Preis" "MBP-1","MacBook Pro","2500.00" |
Wie Du siehst, wird nur die Callback-Funktion geändert und unser generic CSVProcessor übernimmt den Rest.
Optionale Callback-Funktionen
Machmal macht es Sinn eine Callback-Funktion optional zu machen. Wir benötigen dann eine Möglichkeit, in der Klasse zu testen, ob eine Callback-Funktion übergeben wurde oder nicht. Hierfür gibt es in Python eine Funktion namens callable(variable). Ist die übergebene Variabel die Adresse einer ausführbare Funktion, liefert callable() True, ansonsten False:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import csv class CSVProcessor: def run(self, f, callback=None): if callable(callback): with open(f, 'r') as file: reader = csv.DictReader(file) for row in reader: callback(row) else: print("Keine Daten.") if __name__ == '__main__': def printFields(row): print(row['SKU'], row['Produkt'], row['Preis']) p = CSVProcessor() p.run('products.csv', printFields) |
Der Parameter callback in der run-Methode hat den Default-Wert None, wenn keine Callback-Funktion übergeben wurde.
callable(callback) liefert dann False.