Python type()-Funktion

Die Python type()-Funktion ist eine grundlegende Python-Funktion für die Arbeit mit Typen. Als Teil der Python-Implementation gehört sie zum Kernbereich der Sprache.

Wozu dient die type()-Funktion in Python?

Die type()-Funktion kommt in Python für zwei ganz verschiedene Anwendungsfälle zum Einsatz:

  1. Typ eines Python-Objekts ermitteln
  2. Dynamisch einen neuen Typ erzeugen

Wir betrachten zunächst den ersten Fall. Dieser ist im alltäglichen Gebrauch weitaus nützlicher.

Mit type() den Typ eines Objekts ermitteln

Python ist eine dynamisch typisierte Sprache. Das bedeutet, dass Typen erst zur Laufzeit ermittelt werden und an Werte statt an Variablen gebunden sind. Aus diesem Umstand ergibt sich die Notwendigkeit, den Typ eines Objekts zur Laufzeit zu ermitteln.

Wir rufen Pythons type()-Funktion auf und übergeben ein Objekt als einzigen Parameter. Als Rückgabe erhalten wir den Typ des Objekts, z. B. int oder str:

# Type of `42` is `int`
assert type(42) == int
# Type of `str(42)` is `str`
assert type(str(42)) == str
Python

Rufen wir im Python-REPL die type()-Funktion auf, enthält die textuelle Repräsentation „class“ anstatt „type“:

# Returns "<class 'int'>" inside REPL
type(42)
Python

Was zunächst verwirrend scheint, ist durchaus sinnvoll, denn in Python gilt: „Everything is an object“. In Python entspricht der Typ eines Objekts seiner Klasse. So ist der Aufruf der type()-Funktion generell äquivalent zum Auslesen des __class__-Attributs:

# Should hold in most cases
assert type(obj) is obj.__class__
Python

Mit type() einen neuen Typ erzeugen

Betrachten wir nun die zweite Einsatzmöglichkeit der type()-Funktion. Mit drei Argumenten aufgerufen, erlaubt uns die Funktion, dynamisch einen neuen Typ zu erzeugen:

type(name, bases, dict, **kwds)
Python

In dieser Form fungiert die Python type()-Funktion analog zum class-Schlüsselwort. Der Code Type = type("Type", bases, dict) entspricht in etwa der folgenden Klassen-Definition:

class <Type>(<bases>):
    <dict>
Python

Weiter unten zeigen wir ein paar konkrete Beispiele für den Einsatz der Python-type()-Funktion zum Erzeugen neuer Typen. Vorab eine Übersicht der Argumente:

name bases dict **kwds
Name des neuen Typs als String Tupel mit Basisklassen Dict mit Attributen der neuen Klasse Weitere Argumente für Metaklasse-Instanziierung
Tipp

Mit Deploy Now von IONOS deployen Sie Websites und Apps einfach via GitHub.

Wie funktioniert die Python-type()-Funktion?

Bei der Nutzung der type()-Funktion zum Ermitteln des Typs eines Objekts gilt: Der Rückgabewert ist nicht etwa ein String, sondern ein eigenständiges Objekt:

# Value returned by `type(42)` is not a string
assert type(42) != 'int'
# We get back an object named `int`
assert type(42) == int
Python

Schauen wir uns ein paar Beispiele von Rückgabewerten der type()-Funktion für Objekte ganz verschiedener Typen an:

# Python objects of different types
different_objs = None, True, 42, 'John', ('Walter', 'White'), ...
# Print out the type of each object
for obj in different_objs:
    print(f"{obj}: {type(obj)}")
Python
type()-Aufruf Textuelle Darstellung
type(None) <class 'NoneType'>
type(True) <class 'bool'>
type(42) <class 'int'>
type('John') <class 'str'>
type(('Walter', 'White')) <class 'tuple'>
type(...) <class 'ellipsis'>

Es stellt sich die Frage: Was ist der Typ des von type() zurückgegebenen Objekts? Probieren wir es aus. Wir rufen die Python type()-Funktion auf und übergeben den Rückgabewert eines weiteren type()-Aufrufs:

# Returns: "<class 'type'>"
type(type(42))
Python

Wir sehen: Neben Pythons eingebauter type()-Funktion existiert der gleichnamige type-Typ. Dies ist der Typ aller anderen Python-Typen, wie wir exemplarisch zeigen:

# DifferentPython objects
different_objs = None, True, 42, 'John', ('Walter', 'White'), ...
# Check the type of each object's type
for obj in different_objs:
    # Show that the type's type is always `type`
    assert type(type(obj)) is type
Python

Der Typ eines jeden Python-Typs ist also type. Falls sich das verwirrend anhört – es wird noch besser: Selbst der Typ des type-Objekts ist wiederum type. Das lässt sich ewig fortsetzen, wie eine Schlange, die sich selbst in den Schwanz beißt:

# It's `type` all the way down
assert type(type(type(type))) is type
Python

Um die Verwirrung aufzuklären, benötigt man ein tieferes Verständnis von Pythons OOP-System. Pythons eingebautes type-Objekt repräsentiert eine sogenannte Metaklasse. Eine Metaklasse verhält sich zu einer Klasse wie eine Klasse zu einem Objekt. Anders gesagt: Eine Metaklasse ist eine Vorlage (engl. template) für eine Klasse, während eine Klasse ein Template für ein Objekt ist:

Template Instanz
Klasse Objekt
Metaklasse Klasse
Beispiel: type int, str etc.
Beispiel:int 42
Beispiel: str “Walter White”

Wie wird die type()-Funktion in Python eingesetzt?

Für gewöhnlich wird Pythons type()-Funktion eingesetzt, um zur Laufzeit den Typ eines Objekts zu ermitteln. Dies ist nützlich, da Python eine dynamisch typisierte Sprache ist. In einer statisch typisierten Sprache wie Java wird ein Typ per Deklaration an eine Variable gebunden und lässt sich zur Laufzeit nicht verändern:

// Declare variable as `boolean`
boolean answer;
// Attempting to assign `int` value
// Throws type error
answer = 42;
Java

Demgegenüber sind Variablen in Python lediglich Namen, die auf typisierte Werte verweisen. Während der Ausführung des Codes lässt sich ein Name jederzeit auf einen Wert mit einem anderen Typen verweisen. Um den Typ einer Python-Variable zur Laufzeit festzustellen, benötigen wir also die type()-Funktion:

# Assign boolean value
answer = True
# Show that type is `bool`
assert type(answer) is bool
# Reassign integer value
answer = 42
# Show that type is now `int`
assert type(answer) is int
Python

Funktions-Argumente in Python auf ihren Typ überprüfen

Beim Definieren einer Funktion ist es oft notwendig, die Argumente auf Einhaltung gewisser Kriterien zu überprüfen. Beispielsweise darf ein Argument nur innerhalb bestimmter Grenzen liegen oder es sind nur Argumente geeigneter Typen erlaubt. So werden Laufzeitfehler vermieden.

Veranschaulichen wir uns den Einsatz der type()-Funktion an einem Beispiel: Wir definieren eine Funktion, die eine Liste von Zahlen aufaddiert. Damit das funktioniert, müssen wir sicherstellen, dass jedes Argument tatsächlich eine Zahl ist. Wir nutzen type() innerhalb einer assert-Anweisung:

# Function to add up numeric arguments
def add_numbers(*args):
    result = 0
    # Check each argument
    for arg in args:
        # Abort with error message if argument is not an `int` or `float`
        assert type(arg) in (int, float), f"Argument `{arg}` is not a number"
        # Add argument's value to total
        result += arg
    return result
# Show that it works for numbers
assert add_numbers(35, 7) == 42
# The following will fail
add_numbers(29, 'thirteen')
Python

Debugging im Python-REPL mit der type()-Funktion

Einer der Vorteile beim Einsatz einer interpretierten Sprache wie Python ist die interaktive Ausführung von Code im REPL (Read-Eval-Print-Loop). Der Ansatz erlaubt schnelle Prototypisierung und ein direktes Debugging via Inspektion der im Speicher befindlichen Objekte.

Stellen wir uns folgendes Szenario vor: Unser Code enthält eine Variable answer, die einen booleschen Wert enthalten soll. Wir stellen fest, dass der Typ nicht unseren Erwartungen entspricht und nutzen Pythons type()-Funktion, um den tatsächlichen Typ auszugeben. Wie sich herausstellt, haben wir versehentlich den booleschen Wert in Anführungszeichen geschrieben – ein besonders bei Anfängern und Anfängerinnen häufig auftretender Flüchtigkeitsfehler:

# Accidentally set to string
answer = 'False'
# Assertion will fail
assert type(answer) is bool
# Correct to boolean value
answer = False
# Now assertion holds
assert type(answer) is bool
Python

Python-Klassen mit der type()-Funktion dynamisch erzeugen

Wie wir gesehen haben, lassen sich Python-Klassen mit der type()-Funktion dynamisch – d. h. zur Laufzeit – erzeugen. Nützlich ist dies u. a. für Familien von Klassen, was wir am Beispiel von HTML-Tags veranschaulichen. Zunächst erzeugen wir eine Basisklasse Tag, deren Objekte sich selbst als HTML-Code darstellen können:

# Class representing HTML tag
class Tag:
    # Initialize HTML tag with contents
    def __init__(self, *args):
        # Join contents of tag
        self.content = "".join([arg.__str__() for arg in args])
    # String representation returns HTML
    def __str__(self):
        return f"<{self.name}>{self.content}</{self.name}>"
Python

Im Anschluss spezialisieren wir die Basisklasse per Vererbung auf die jeweiligen spezifischen Tags wie <p> oder <h1>. Dazu rufen wir die type()-Funktion mit drei Argumenten auf:

# Create `P` class
P = type('P', (Tag,), {"name": 'p'})
Python
  1. Name der neuen Klasse als String.

  2. Tupel mit Basisklassen.

    Python erlaubt multiple Vererbung; um von nur einer Klasse abzuleiten, nutzen wir die Schreibweise (ClassName,).

  3. Dict mit dem Namen der Klasse und ggf. weiteren Einträgen.

    Bei den Einträgen kann es sich auch um Funktionen handeln.

Im Anschluss instanziieren wir ein p-Tag und überprüfen, dass die Darstellung korrekt funktioniert:

# Instantiate `p` tag
greeting = P("Hello world")
assert str(greeting) == '&lt;p&gt;Hello world&lt;/p&gt;'
Python

Derselbe Effekt lässt sich per analoger Klassen-Definition erzielen:

# Create `P` class
class P(Tag):
    name = 'p'
Python

Als weiteres Beispiel erzeugen wir mit type() Klassen für Überschriften. Da die Erzeugung der Klassen dynamisch erfolgt, lassen sich per List-Comprehension die Klassen für alle sechs Überschriften-Level auf einen Schlag erzeugen:

h_1_to_6 = ( f"h{n}" for n in range(1, 7) )
headings = [type(heading, (Tag,), {"name": heading}) for heading in h_1_to_6]
Python

Wie wir gezeigt haben, lohnt sich der Einsatz der type()-Funktion zum komfortablen Erzeugen mehrerer verwandter Unterklassen. Wir zeigen den Ansatz am komplexeren Beispiel, Klassen zur Modellierung von Spielkarten zu definieren. Zunächst definieren wir eine Überklasse Card per class-Schlüsselwort:

# Class representing abstract playing card
class Card:
    def __init__(self, number):
        self.number = number
    # String representation
    def __str__(self):
        return f"{self.number} of {self.suite}"
Python

Im Anschluss erzeugen wir Unterklassen für die vier Kartenfarben mittels type():

# Create concrete types for each suite
Clubs = type('Clubs', (Card,), {'suite': 'Clubs'})
Diamonds = type('Diamonds', (Card,), {'suite': 'Diamonds'})
Hearts = type('Hearts', (Card,), {'suite': 'Hearts'})
Spades = type('Spades', (Card,), {'suite': 'Spades'})
Python

Nun lassen sich die einzelnen Karten problemlos instanziieren:

# Instantiate a 7 of Spades
card = Spades(7)
# Show that it worked
assert str(card) == '7 of Spades'
Python

Wo stößt die type()-Funktion an ihre Grenzen?

Pythons type()-Funktion ist nützlich. Es gibt jedoch einige Anwendungsfälle, bei deren Lösung die Funktion an ihre Grenzen stößt. Glücklicherweise kennt Python passende Ansätze; schauen wir uns einige davon an.

Vererbungs-Hierarchien mit isinstance() aufschlüsseln

type() ermittelt nur den tatsächlichen Typ eines Python-Objekts, lässt dabei jedoch die Vererbungs-Hierarchie außer Acht. Das sich daraus ergebende Dilemma veranschaulichen wir anhand unseres Spielkarten-Beispiels aus dem letzten Abschnitt. Der Typ einer Pik 7 sollte sowohl „Spielkarte“ als auch „Pik“ sein. Mit type() lässt sich dies jedoch nicht ermitteln:

# Create a Seven of Spades
card = Spades(7)
# Our card is a Spade alright
assert type(card) is Spades
# But not a card??
assert type(card) is not Card
Python

Um den zugrundeliegenden Polymorphismus korrekt aufzuschlüsseln, bedienen wir uns der isinstance()-Funktion.

# Seven of Spades is a `Spade`
assert isinstance(card, Spades)
# And is also a `Card`
assert isinstance(card, Card)
Python

Mit match-case die Erkennung des Python-Objekt-Typs vereinfachen

Wie wir weiter oben gezeigt haben, wird die type()-Funktion häufig eingesetzt, um den Typ eines Objekts zur Laufzeit zu ermitteln. Um mehrere mögliche Typen voneinander zu unterscheiden, kommt ggf. ein if-elif-else-Konstrukt zum Einsatz:

# Determine type of object
if type(obj) is int:
    print("Int")
elif type(obj) is float:
    print("Float")
elif type(obj) is ...:
    print("...")
else:
    print("Something else")
Python

Seit Version 3.10 kennt Python jedoch die match-case-Anweisung. Diese erlaubt u. a., Typen ohne Aufruf der type()-Funktion zu erkennen.

Innerhalb eines case-Blocks lassen sich Konstruktor-Funktionen wie int(obj) oder str(obj) einsetzen. Der Block matcht, wenn das Objekt den jeweiligen Typ hat:

# Example object
obj = 42
# Determine object type
match obj:
    case int(obj):
        print(f"{obj} is `int`")
    case float(obj):
        print(f"{obj} is `float`")
    case _:
        print(f"{obj} is something else")
Python
Tipp

Nutzen Sie für den Einstieg in die Sprache auch unser Python-Tutorial sowie unsere Übersicht der Python-Operatoren.