Eine Callback-Funktion 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 Callback-Funktionen ausgelagert.
Python Callback-Funktion: 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('addresses.csv') |
Inhalt von addresses.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 printAddressFields(row): print(row['Name'], row['Vorname'], row['Email']) p = CSVProcessor() p.run('addresses.csv', printAddressFields) |
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 printAddressFields(), 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 printProductFields(row): print(row['SKU'], row['Produkt'], row['Preis']) p = CSVProcessor() p.run('products.csv', printProductFields) |
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 printProductFields(row): print(row['SKU'], row['Produkt'], row['Preis']) p = CSVProcessor() p.run('products.csv', printProductFields) |
Der Parameter callback in der run-Methode hat den Default-Wert None, wenn keine Callback-Funktion übergeben wurde.
callable(callback) liefert dann False.