Zum Inhalt

Einführung in Python-Typen

🌐 Übersetzung durch KI und Menschen

Diese Übersetzung wurde von KI erstellt, angeleitet von Menschen. 🤝

Sie könnte Fehler enthalten, etwa Missverständnisse des ursprünglichen Sinns oder unnatürliche Formulierungen, usw. 🤖

Sie können diese Übersetzung verbessern, indem Sie uns helfen, die KI-LLM besser anzuleiten.

Englische Version

Python hat Unterstützung für optionale „Typhinweise“ (auch „Typannotationen“ genannt).

Diese „Typhinweise“ oder -Annotationen sind eine spezielle Syntax, die es erlaubt, den Typ einer Variablen zu deklarieren.

Durch das Deklarieren von Typen für Ihre Variablen können Editoren und Tools bessere Unterstützung bieten.

Dies ist lediglich eine schnelle Anleitung / Auffrischung über Pythons Typhinweise. Sie deckt nur das Minimum ab, das nötig ist, um diese mit FastAPI zu verwenden ... was tatsächlich sehr wenig ist.

FastAPI basiert vollständig auf diesen Typhinweisen, sie geben der Anwendung viele Vorteile und Möglichkeiten.

Aber selbst wenn Sie FastAPI nie verwenden, wird es für Sie nützlich sein, ein wenig darüber zu lernen.

Hinweis

Wenn Sie ein Python-Experte sind und bereits alles über Typhinweise wissen, überspringen Sie dieses Kapitel und fahren Sie mit dem nächsten fort.

Motivation

Fangen wir mit einem einfachen Beispiel an:

def get_full_name(first_name, last_name):
    full_name = first_name.title() + " " + last_name.title()
    return full_name


print(get_full_name("john", "doe"))

Dieses Programm gibt aus:

John Doe

Die Funktion macht Folgendes:

  • Nimmt einen first_name und last_name.
  • Schreibt den ersten Buchstaben eines jeden Wortes groß, mithilfe von title().
  • Verkettet sie mit einem Leerzeichen in der Mitte.
def get_full_name(first_name, last_name):
    full_name = first_name.title() + " " + last_name.title()
    return full_name


print(get_full_name("john", "doe"))

Es bearbeiten

Es ist ein sehr einfaches Programm.

Aber nun stellen Sie sich vor, Sie würden es selbst schreiben.

Irgendwann sind die Funktions-Parameter fertig, Sie starten mit der Definition des Körpers ...

Aber dann müssen Sie „diese Methode aufrufen, die den ersten Buchstaben in Großbuchstaben umwandelt“.

War es upper? War es uppercase? first_uppercase? capitalize?

Dann versuchen Sie es mit dem langjährigen Freund des Programmierers, der Editor-Autovervollständigung.

Sie geben den ersten Parameter der Funktion ein, first_name, dann einen Punkt (.) und drücken Strg+Leertaste, um die Vervollständigung auszulösen.

Aber leider erhalten Sie nichts Nützliches:

Typen hinzufügen

Lassen Sie uns eine einzelne Zeile aus der vorherigen Version ändern.

Wir ändern den folgenden Teil, die Parameter der Funktion, von:

    first_name, last_name

zu:

    first_name: str, last_name: str

Das war's.

Das sind die „Typhinweise“:

def get_full_name(first_name: str, last_name: str):
    full_name = first_name.title() + " " + last_name.title()
    return full_name


print(get_full_name("john", "doe"))

Das ist nicht das gleiche wie das Deklarieren von Defaultwerten, wie es hier der Fall ist:

    first_name="john", last_name="doe"

Das ist eine andere Sache.

Wir verwenden Doppelpunkte (:), nicht Gleichheitszeichen (=).

Und das Hinzufügen von Typhinweisen ändert normalerweise nichts an dem, was ohne sie passieren würde.

Aber jetzt stellen Sie sich vor, Sie sind wieder mitten in der Erstellung dieser Funktion, aber mit Typhinweisen.

An derselben Stelle versuchen Sie, die Autovervollständigung mit „Strg+Leertaste“ auszulösen, und Sie sehen:

Hier können Sie durch die Optionen blättern, bis Sie diejenige finden, bei der es „Klick“ macht:

Mehr Motivation

Sehen Sie sich diese Funktion an, sie hat bereits Typhinweise:

def get_name_with_age(name: str, age: int):
    name_with_age = name + " is this old: " + age
    return name_with_age

Da der Editor die Typen der Variablen kennt, erhalten Sie nicht nur Code-Vervollständigung, sondern auch eine Fehlerprüfung:

Jetzt, da Sie wissen, dass Sie das reparieren müssen, konvertieren Sie age mittels str(age) in einen String:

def get_name_with_age(name: str, age: int):
    name_with_age = name + " is this old: " + str(age)
    return name_with_age

Deklarieren von Typen

Sie haben gerade den Haupt-Einsatzort für die Deklaration von Typhinweisen gesehen. Als Funktionsparameter.

Das ist auch meistens, wie sie in FastAPI verwendet werden.

Einfache Typen

Sie können alle Standard-Python-Typen deklarieren, nicht nur str.

Zum Beispiel diese:

  • int
  • float
  • bool
  • bytes
def get_items(item_a: str, item_b: int, item_c: float, item_d: bool, item_e: bytes):
    return item_a, item_b, item_c, item_d, item_e

typing-Modul

Für einige zusätzliche Anwendungsfälle müssen Sie möglicherweise Dinge aus dem Standardmodul typing importieren. Zum Beispiel, wenn Sie deklarieren möchten, dass etwas „jeden Typ“ haben kann, können Sie Any aus typing verwenden:

from typing import Any


def some_function(data: Any):
    print(data)

Generische Typen

Einige Typen können „Typ-Parameter“ in eckigen Klammern annehmen, um ihre inneren Typen zu definieren, z. B. eine „Liste von Strings“ würde als list[str] deklariert.

Diese Typen, die Typ-Parameter annehmen können, werden generische Typen oder Generics genannt.

Sie können dieselben eingebauten Typen als Generics verwenden (mit eckigen Klammern und Typen darin):

  • list
  • tuple
  • set
  • dict

Liste

Definieren wir zum Beispiel eine Variable, die eine list von str – eine Liste von Strings – sein soll.

Deklarieren Sie die Variable mit der gleichen Doppelpunkt-Syntax (:).

Als Typ nehmen Sie list.

Da die Liste ein Typ ist, welcher innere Typen enthält, werden diese von eckigen Klammern umfasst:

def process_items(items: list[str]):
    for item in items:
        print(item)

Info

Die inneren Typen in den eckigen Klammern werden als „Typ-Parameter“ bezeichnet.

In diesem Fall ist str der Typ-Parameter, der an list übergeben wird.

Das bedeutet: Die Variable items ist eine Liste – list – und jedes der Elemente in dieser Liste ist ein String – str.

Auf diese Weise kann Ihr Editor Sie auch bei der Bearbeitung von Einträgen aus der Liste unterstützen:

Ohne Typen ist das fast unmöglich zu erreichen.

Beachten Sie, dass die Variable item eines der Elemente in der Liste items ist.

Und trotzdem weiß der Editor, dass es sich um ein str handelt, und bietet entsprechende Unterstützung.

Tupel und Menge

Das Gleiche gilt für die Deklaration eines Tupels – tuple – und einer Menge – set:

def process_items(items_t: tuple[int, int, str], items_s: set[bytes]):
    return items_t, items_s

Das bedeutet:

  • Die Variable items_t ist ein tuple mit 3 Elementen, einem int, einem weiteren int und einem str.
  • Die Variable items_s ist ein set, und jedes seiner Elemente ist vom Typ bytes.

Dict

Um ein dict zu definieren, übergeben Sie zwei Typ-Parameter, getrennt durch Kommas.

Der erste Typ-Parameter ist für die Schlüssel des dict.

Der zweite Typ-Parameter ist für die Werte des dict:

def process_items(prices: dict[str, float]):
    for item_name, item_price in prices.items():
        print(item_name)
        print(item_price)

Das bedeutet:

  • Die Variable prices ist ein dict:
    • Die Schlüssel dieses dict sind vom Typ str (z. B. die Namen der einzelnen Artikel).
    • Die Werte dieses dict sind vom Typ float (z. B. der Preis jedes Artikels).

Union

Sie können deklarieren, dass eine Variable einer von verschiedenen Typen sein kann, zum Beispiel ein int oder ein str.

Um das zu definieren, verwenden Sie den vertikalen Balken (|), um beide Typen zu trennen.

Das wird „Union“ genannt, weil die Variable etwas aus der Vereinigung dieser beiden Typmengen sein kann.

def process_item(item: int | str):
    print(item)

Das bedeutet, dass item ein int oder ein str sein könnte.

Vielleicht None

Sie können deklarieren, dass ein Wert einen Typ haben könnte, wie str, dass er aber auch None sein könnte.

def say_hi(name: str | None = None):
    if name is not None:
        print(f"Hey {name}!")
    else:
        print("Hello World")

Wenn Sie str | None anstelle von nur str verwenden, wird Ihr Editor Ihnen dabei helfen, Fehler zu erkennen, bei denen Sie annehmen könnten, dass ein Wert immer ein str ist, obwohl er auch None sein könnte.

Klassen als Typen

Sie können auch eine Klasse als Typ einer Variablen deklarieren.

Nehmen wir an, Sie haben eine Klasse Person, mit einem Namen:

class Person:
    def __init__(self, name: str):
        self.name = name


def get_person_name(one_person: Person):
    return one_person.name

Dann können Sie eine Variable vom Typ Person deklarieren:

class Person:
    def __init__(self, name: str):
        self.name = name


def get_person_name(one_person: Person):
    return one_person.name

Und wiederum bekommen Sie die volle Editor-Unterstützung:

Beachten Sie, das bedeutet: „one_person ist eine Instanz der Klasse Person“.

Es bedeutet nicht: „one_person ist die Klasse genannt Person“.

Pydantic-Modelle

Pydantic ist eine Python-Bibliothek für die Validierung von Daten.

Sie deklarieren die „Form“ der Daten als Klassen mit Attributen.

Und jedes Attribut hat einen Typ.

Dann erzeugen Sie eine Instanz dieser Klasse mit einigen Werten, und Pydantic validiert die Werte, konvertiert sie in den passenden Typ (falls notwendig) und gibt Ihnen ein Objekt mit allen Daten.

Und Sie erhalten volle Editor-Unterstützung für dieses Objekt.

Ein Beispiel aus der offiziellen Pydantic Dokumentation:

from datetime import datetime

from pydantic import BaseModel


class User(BaseModel):
    id: int
    name: str = "John Doe"
    signup_ts: datetime | None = None
    friends: list[int] = []


external_data = {
    "id": "123",
    "signup_ts": "2017-06-01 12:22",
    "friends": [1, "2", b"3"],
}
user = User(**external_data)
print(user)
# > User id=123 name='John Doe' signup_ts=datetime.datetime(2017, 6, 1, 12, 22) friends=[1, 2, 3]
print(user.id)
# > 123

FastAPI basiert vollständig auf Pydantic.

Viel mehr von all dem werden Sie in praktischer Anwendung im Tutorial – Benutzerhandbuch sehen.

Typhinweise mit Metadaten-Annotationen

Python bietet auch die Möglichkeit, zusätzliche Metadaten in Typhinweisen unterzubringen, mittels Annotated.

Sie können Annotated von typing importieren.

from typing import Annotated


def say_hello(name: Annotated[str, "this is just metadata"]) -> str:
    return f"Hello {name}"

Python selbst macht nichts mit Annotated. Für Editoren und andere Tools ist der Typ immer noch str.

Aber Sie können Annotated nutzen, um FastAPI mit Metadaten zu versorgen, die ihm sagen, wie sich Ihre Anwendung verhalten soll.

Wichtig ist, dass der erste Typ-Parameter, den Sie Annotated übergeben, der tatsächliche Typ ist. Der Rest sind Metadaten für andere Tools.

Im Moment müssen Sie nur wissen, dass Annotated existiert, und dass es Standard-Python ist. 😎

Später werden Sie sehen, wie mächtig es sein kann.

Tipp

Der Umstand, dass es Standard-Python ist, bedeutet, dass Sie immer noch die bestmögliche Entwickler-Erfahrung in Ihrem Editor haben, sowie mit den Tools, die Sie nutzen, um Ihren Code zu analysieren, zu refaktorisieren, usw. ✨

Und ebenfalls, dass Ihr Code sehr kompatibel mit vielen anderen Python-Tools und -Bibliotheken sein wird. 🚀

Typhinweise in FastAPI

FastAPI macht sich diese Typhinweise zunutze, um mehrere Dinge zu tun.

Mit FastAPI deklarieren Sie Parameter mit Typhinweisen, und Sie erhalten:

  • Editorunterstützung.
  • Typ-Prüfungen.

... und FastAPI verwendet dieselben Deklarationen, um:

  • Anforderungen zu definieren: aus Request-Pfadparametern, Query-Parametern, Header-Feldern, Bodys, Abhängigkeiten, usw.
  • Daten umzuwandeln: aus dem Request in den erforderlichen Typ.
  • Daten zu validieren: aus jedem Request:
    • Automatische Fehler generieren, die an den Client zurückgegeben werden, wenn die Daten ungültig sind.
  • Die API mit OpenAPI zu dokumentieren:
    • Die dann von den Benutzeroberflächen der automatisch generierten interaktiven Dokumentation verwendet wird.

Das mag alles abstrakt klingen. Machen Sie sich keine Sorgen. Sie werden all das in Aktion sehen im Tutorial – Benutzerhandbuch.

Das Wichtigste ist, dass FastAPI durch die Verwendung von Standard-Python-Typen an einer einzigen Stelle (anstatt weitere Klassen, Dekoratoren usw. hinzuzufügen) einen Großteil der Arbeit für Sie erledigt.

Info

Wenn Sie bereits das ganze Tutorial durchgearbeitet haben und mehr über Typen erfahren wollen, dann ist eine gute Ressource der „Cheat Sheet“ von mypy.