Weitere Themen#

Bisher haben wir die meisten verschiedenen Aspekte von Python abgedeckt, die Sie verwenden werden. In diesem Kapitel werden wir einige weitere Aspekte behandeln, die unser Wissen über Python abrunden.

Tupel zurückgeben und weitergeben#

Haben Sie sich schon einmal gewünscht, zwei verschiedene Werte aus einer Funktion zurückgeben zu können? Das ist möglich. Alles, was Sie tun müssen, ist ein Tupel zu verwenden.

>>> def get_error_details():
...     return (2, 'details')
...
>>> errnum, errstr = get_error_details()
>>> errnum
2
>>> errstr
'details'

Beachten Sie, dass die Verwendung von a, b = <irgendein Ausdruck> das Ergebnis des Ausdrucks als ein Tupel mit zwei Werten interpretiert.

Dies bedeutet auch, dass der schnellste Weg, zwei Variablen in Python zu vertauschen, folgender ist:

>>> a = 5; b = 8
>>> a, b
(5, 8)
>>> a, b = b, a
>>> a, b
(8, 5)

Spezielle Methoden#

Es gibt bestimmte Methoden wie __init__ und __del__, die in Klassen eine besondere Bedeutung haben.

Hinweis

Spezielle Methoden werden manchmal als dunder_methods bezeichnet, verballhornt von double_underscore

  • (brit flag double: doppelt)

  • (brit flag underscore: Unterstrich)

Spezielle Methoden werden verwendet, um bestimmte Verhaltensweisen von eingebauten Typen nachzuahmen. Wenn Sie z. B. die Indexoperation x[key] für Ihre Klasse verwenden möchten (so wie Sie sie für Listen und Tupel verwenden), müssen Sie nur die Methode __getitem__() implementieren, und Ihre Aufgabe ist erledigt. Wenn Sie darüber nachdenken, ist das genau das, was Python für die list-Klasse selbst tut!

Einige nützliche spezielle Methoden sind in der folgenden Tabelle aufgelistet. Wenn Sie mehr über alle speziellen Methoden erfahren möchten, sehen Sie sich das Handbuch an.

  • __init__(self, ...)

    • Diese Methode wird aufgerufen, kurz bevor das neu erstellte Objekt für die Verwendung zurückgegeben wird.

  • __del__(self)

    • Wird aufgerufen, kurz bevor das Objekt zerstört wird (der Zeitpunkt ist unvorhersehbar, daher sollten Sie diese Methode vermeiden).

  • __str__(self)

    • Wird aufgerufen, wenn wir die print-Funktion verwenden oder wenn str() verwendet wird.

  • __lt__(self, other)

    • Wird aufgerufen, wenn der kleiner als-Operator (<) verwendet wird. Ähnlich gibt es spezielle Methoden für alle Operatoren (+, >, usw.).

  • __getitem__(self, key)

    • Wird aufgerufen, wenn x[key] indexiert wird.

  • __len__(self)

    • Wird aufgerufen wenn die build_in Python-Funktion len()mit dem Objekt aufgerufen wird.

Einzelne Anweisungsblöcke (single statement blocks)#

Wir haben gesehen, dass jeder Block von Anweisungen durch seine eigene Einrückungsebene vom Rest getrennt ist. Nun, es gibt eine Ausnahme. Wenn Ihr Block von Anweisungen nur eine einzige Anweisung enthält, dann können Sie diese in derselben Zeile wie beispielsweise eine Bedingungsanweisung oder eine Schleifenanweisung angeben. Das folgende Beispiel sollte dies verdeutlichen:

>>> flag = True
>>> if flag: print('Yes')
...
Yes

Beachten Sie, dass die einzelne Anweisung direkt verwendet wird und nicht als separater Block. Obwohl Sie dies verwenden können, um Ihr Programm kleiner zu machen, empfehle ich dringend, diese Abkürzung zu vermeiden – außer beim Prüfen von Fehlern –, hauptsächlich weil es viel einfacher ist, eine zusätzliche Anweisung hinzuzufügen, wenn Sie die korrekte Einrückung verwenden.

Lambda-Formen#

Eine lambda-Anweisung (auch: anonyme Funktion) wird verwendet, um neue Funktionsobjekte zu erzeugen. Im Wesentlichen nimmt lambda einen Parameter gefolgt von einem einzigen Ausdruck. Die Lambda wird zum Rumpf der Funktion. Der Wert dieses Ausdrucks wird von der neuen Funktion zurückgegeben.

Beispiel more_lambda_de.py

Quellcode
1points = [{'x': 2, 'y': 3},
2          {'x': 4, 'y': 1}]
3points.sort(key=lambda i: i['y'])
4print(points)

Die Zeilennummern sind nicht Bestandteil des Quellcodes

Ausgabe
$ python more_lambda.py
[{'y': 1, 'x': 4}, {'y': 3, 'x': 2}]

Wie es funktioniert:

Beachten Sie, dass die Methode sort einer list einen Parameter namens key akzeptiert, der bestimmt, wie die Liste sortiert wird (gewöhnlich wissen wir nur etwas über aufsteigende oder absteigende Ordnung). In unserem Fall möchten wir eine benutzerdefinierte Sortierung durchführen, und dafür müssen wir eine Funktion schreiben. Anstatt einen separaten def-Block für eine Funktion zu schreiben, die nur an dieser einen Stelle verwendet wird, benutzen wir einen Lambda-Ausdruck, um eine neue Funktion zu erzeugen.

List Comprehension#

List Comprehensions werden verwendet, um eine neue Liste aus einer bestehenden Liste abzuleiten. Angenommen, Sie haben eine Liste von Zahlen und Sie möchten eine entsprechende Liste erhalten, in der alle Zahlen mit 2 multipliziert wurden, jedoch nur dann, wenn die Zahl selbst größer als 2 ist. List Comprehensions sind ideal für solche Situationen.

Beispiel (speichern als more_list_comprehension_de.py):

Beispiel more_list_comprehension_de.py

Quellcode
1listone = [2, 3, 4]
2listtwo = [2*i for i in listone if i > 2]
3print(listtwo)

Die Zeilennummern sind nicht Bestandteil des Quellcodes

Ausgabe
$ python more_list_comprehension.py
[6, 8]

Wie es funktioniert:

Hier leiten wir eine neue Liste ab, indem wir die auszuführende Manipulation angeben (2*i), wenn eine bestimmte Bedingung erfüllt ist (if i > 2). Beachten Sie, dass die ursprüngliche Liste unverändert bleibt.

Der Vorteil von List Comprehensions besteht darin, dass sie die Menge an Boilerplate-Code reduzieren, den wir benötigen, wenn wir Schleifen verwenden, um jedes Element einer Liste zu verarbeiten und es in einer neuen Liste zu speichern.

Entgegennahme von Tupeln und Dictionaries in Funktionen#

  • (brit flag *args: steht für (beliebig viele) arguments)

  • (brit flag **kwargs: steht für (beliebig viele) keyword arguments)

Es gibt eine besondere Möglichkeit, Parameter an eine Funktion als Tupel oder Dictionary zu übergeben, indem man vor dem Variablennamen das Präfix * bzw. ** verwendet. Dies ist nützlich, wenn die Funktion eine variable Anzahl von Argumenten annimmt.

>>> def potenzsumme(power, *args):
...     '''Gibt die Summe aller Elemente hoch power zurück'''
...     total = 0
...     for element in args:
...         total += i**power
...         #oder: total += pow(i, power)
...     return total
...
>>> potenzsumme(2, 3, 4) # 3² + 4² 
25
>>> potenzsumme(3, 10, 5 ) # 10³ + 5³
1125

Da wir ein *-Präfix vor der Variable args haben, werden alle zusätzlichen Argumente, die an die Funktion übergeben werden, in args als Tupel gespeichert. Wenn stattdessen ein **-Präfix verwendet worden wäre, würden die zusätzlichen Parameter als Schlüssel/Wert-Paare eines Dictionaries betrachtet.

Die assert - Anweisung#

Die assert-Anweisung wird verwendet, um sicherzustellen, dass etwas wahr ist. Wenn Sie z. B. sehr sicher sind, dass Sie mindestens ein Element in einer Liste haben, die Sie verwenden, und Sie dies überprüfen möchten und einen Fehler auslösen wollen, wenn es nicht zutrifft, dann ist die assert-Anweisung für diese Situation ideal. Wenn die Assert-Anweisung fehlschlägt, wird ein AssertionError ausgelöst. Die Methode pop() entfernt letzte Element einer Liste und gibt es zurück.

>>> mylist = ['item']
>>> assert len(mylist) >= 1
>>> mylist.pop()
'item'
>>> assert len(mylist) >= 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError

Die assert-Anweisung sollte mit Bedacht eingesetzt werden. Die meiste Zeit ist es besser, Ausnahmen abzufangen, entweder das Problem zu behandeln oder dem Benutzer eine Fehlermeldung anzuzeigen und dann zu beenden.

Dekoratoren#

  • (brit flag decorators)

Dekoratoren sind eine Abkürzung, um Wrapper-Funktionen anzuwenden. Dies ist hilfreich, um Funktionalität immer wieder mit demselben Code zu „umhüllen“. Zum Beispiel habe ich für mich selbst einen retry-Dekorator erstellt, den ich einfach auf jede Funktion anwenden kann, und wenn während eines Aufrufs eine Ausnahme ausgelöst wird, wird der Aufruf erneut ausgeführt – bis zu maximal 5 Mal und mit einer Verzögerung zwischen jedem Versuch. Dies ist besonders nützlich für Situationen, in denen Sie versuchen, einen Netzwerkaufruf zu einem entfernten Computer durchzuführen:

Beispiel more_decorator_de.py

Quellcode
 1#from time import sleep
 2import time
 3#from functools import wraps
 4import functools
 5import logging
 6logging.basicConfig()
 7log = logging.getLogger("retry") 
 8
 9# retry: probier's nochmal
10# *args: beliebig viele argumente
11# **kwargs: beliebig viele keyword-arguments
12
13def retry(f):
14    @functools.wraps(f)
15    def wrapper_function(*args, **kwargs):
16        MAXIMALE_VERSUCHE = 5
17        for versuch in range(1, MAXIMALE_VERSUCHE + 1):
18            try:
19                return f(*args, **kwargs)
20            except Exception:
21                log.exception("Versuch {} von {} schlug fehl: {}".format(
22                              versuch,
23                              MAXIMALE_VERSUCHE,
24                              (args, kwargs)))
25                time.sleep(10 * versuch)
26        log.critical("Alle {} versuche schlugen fehl: {}",
27                     MAXIMALE_VERSUCHE,
28                     (args, kwargs))
29    return wrapper_function
30
31
32zähler = 0
33
34
35@retry
36def save_to_database(arg):
37    print("Schreibt in eine Datenbank, überträgt Daten per Netzwerk etc.")
38    print("Die Funktion wird automatisch nocheinmal ausgeführt wennn")
39    print("eine Exception auftritt")
40    global zähler 
41    zähler += 1
42    print("Dies ist Versuch", zähler)
43    # Beim 1. Versuch tritt eine Exception auf, beim
44    # 2. Versuch wird die Funktion ausgeführt
45    if zähler < 2:
46        raise ValueError(arg)
47    print("Funktion erfolgreich ausgeführt")
48
49
50if __name__ == '__main__':
51    save_to_database("Irgendein schlechter Wert")

Die Zeilennummern sind nicht Bestandteil des Quellcodes

Ausgabe
$ python3 more_decorator_de.py
Schreibt in eine Datenbank, überträgt Daten per Netzwerk etc.
Die Funktion wird automatisch nocheinmal ausgeführt wennn
eine Exception auftritt
Dies ist Versuch 1
ERROR:retry:Versuch 1 von 5 schlug fehl: (('Irgendein schlechter Wert',), {})
Traceback (most recent call last):
  File "/home/horst/code/byte-of-python_deutsch_horst/programs/more_decorator_de.py", line 19, in wrapper_function
    return f(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^
  File "/home/horst/code/byte-of-python_deutsch_horst/programs/more_decorator_de.py", line 46, in save_to_database
    raise ValueError(arg)
ValueError: Irgendein schlechter Wert
Schreibt in eine Datenbank, überträgt Daten per Netzwerk etc.
Die Funktion wird automatisch nocheinmal ausgeführt wennn
eine Exception auftritt
Dies ist Versuch 2
Funktion erfolgreich ausgeführt

Wie es funktioniert:

Siehe auch

Unterschiede zwischen Python 2 und Python 3#

Zusammenfassung#

Wir haben in diesem Kapitel einige weitere Merkmale von Python behandelt, und trotzdem haben wir noch nicht alle Merkmale von Python abgedeckt. Allerdings haben wir an diesem Punkt das meiste behandelt, was Sie in der Praxis jemals verwenden werden. Dies ist ausreichend, damit Sie mit den Programmen beginnen können, die Sie erstellen möchten.

Als Nächstes werden wir besprechen, wie man Python weiter erkunden kann.