Ich würde vorschlagen, PEP 483 und PEP 484 zu lesen und diese Präsentation von Guido über Typhinweise anzusehen.
Kurz gesagt : Typhinweise sind wörtlich das, was die Wörter bedeuten. Sie geben den Typ der Objekte an, die Sie verwenden .
Aufgrund der Dynamik von Python ist es besonders schwierig , den Typ eines verwendeten Objekts abzuleiten oder zu überprüfen . Diese Tatsache macht es Entwicklern schwer zu verstehen, was genau in Code vor sich geht, den sie nicht geschrieben haben, und vor allem für Tools zur Typprüfung, die in vielen IDEs [PyCharm, PyDev] vorkommen, die aufgrund dieser Tatsache eingeschränkt sind Sie haben keinen Indikator für den Typ der Objekte. Infolgedessen versuchen sie, den Typ mit einer Erfolgsquote von etwa 50% (wie in der Präsentation erwähnt) abzuleiten.
So nehmen Sie zwei wichtige Folien aus der Präsentation "Typhinweis":
Warum Hinweise eingeben?
- Hilft bei Typprüfungen: bei der Typprüfung Indem Sie angeben, welcher Typ das Objekt sein soll, kann die Typprüfung leicht erkennen, ob Sie beispielsweise ein Objekt mit einem Typ übergeben, der nicht erwartet wird.
- Hilft bei der Dokumentation: Eine dritte Person, die Ihren Code anzeigt, weiß, was wo erwartet wird, also wie man ihn verwendet, ohne sie zu erhalten
TypeErrors
.
- Hilft IDEs bei der Entwicklung genauerer und robusterer Tools: Entwicklungsumgebungen sind besser geeignet, geeignete Methoden vorzuschlagen, wenn Sie wissen, um welchen Typ es sich bei Ihrem Objekt handelt. Sie haben dies wahrscheinlich irgendwann mit einer IDE erlebt, bei der
.
Methoden und Attribute angezeigt wurden, die nicht für ein Objekt definiert sind.
Warum statische Typprüfer verwenden?
- Finden Sie Fehler früher : Das ist selbstverständlich, glaube ich.
- Je größer Ihr Projekt ist, desto mehr brauchen Sie es : Auch dies ist sinnvoll. Statische Sprachen bieten eine Robustheit und Kontrolle, die dynamischen Sprachen fehlt. Je größer und komplexer Ihre Anwendung wird, desto mehr Kontrolle und Vorhersagbarkeit (unter Verhaltensaspekten) benötigen Sie.
- Große Teams führen bereits statische Analysen durch : Ich vermute, dies bestätigt die ersten beiden Punkte.
Zum Abschluss dieser kleinen Einführung : Dies ist eine optionale Funktion, die meines Wissens eingeführt wurde, um einige der Vorteile der statischen Typisierung zu nutzen.
Sie müssen sich im Allgemeinen keine Sorgen machen und müssen es definitiv nicht verwenden (insbesondere in Fällen, in denen Sie Python als zusätzliche Skriptsprache verwenden). Dies sollte bei der Entwicklung großer Projekte hilfreich sein, da es die dringend benötigte Robustheit, Kontrolle und zusätzliche Debugging-Funktionen bietet .
Tippe mit mypy :
Um diese Antwort zu vervollständigen, halte ich eine kleine Demonstration für angebracht. Ich werde mypy
die Bibliothek verwenden, die Type Hints inspiriert hat, wie sie im PEP präsentiert werden. Dies ist hauptsächlich für alle geschrieben, die auf diese Frage stoßen und sich fragen, wo sie anfangen sollen.
Bevor ich das tue, möchte ich Folgendes wiederholen: PEP 484 erzwingt nichts; Es wird lediglich eine Richtung für Funktionsanmerkungen festgelegt und Richtlinien vorgeschlagen, wie die Typprüfung durchgeführt werden kann / sollte. Sie können Ihre Funktionen mit Anmerkungen versehen und so viele Dinge andeuten, wie Sie möchten. Ihre Skripte werden unabhängig vom Vorhandensein von Anmerkungen weiterhin ausgeführt, da Python sie selbst nicht verwendet.
Wie im PEP angegeben, sollten Andeutungstypen im Allgemeinen drei Formen annehmen:
- Funktionsanmerkungen. ( PEP 3107 )
- Stub-Dateien für integrierte / Benutzermodule.
- Besondere
# type: type
Kommentare, die die ersten beiden Formen ergänzen. (Siehe: Was sind variable Anmerkungen in Python 3.6? Für ein Python 3.6-Update für # type: type
Kommentare)
Darüber hinaus möchten Sie Typhinweise in Verbindung mit dem in eingeführten neuen typing
Modul verwenden Py3.5
. Darin werden viele (zusätzliche) ABCs (Abstract Base Classes) zusammen mit Hilfsfunktionen und Dekoratoren zur Verwendung bei der statischen Überprüfung definiert. Die meisten ABCs
in collections.abc
sind enthalten, jedoch in einer Generic
Form, um ein Abonnement zu ermöglichen (durch Definieren einer __getitem__()
Methode).
Für alle, die an einer ausführlicheren Erklärung interessiert sind, mypy documentation
ist das sehr schön geschrieben und enthält viele Codebeispiele, die die Funktionalität ihres Prüfers demonstrieren / beschreiben. Es ist definitiv eine Lektüre wert.
Funktionsanmerkungen und spezielle Kommentare:
Zunächst ist es interessant, einige Verhaltensweisen zu beobachten, die bei Verwendung spezieller Kommentare auftreten können. # type: type
Während der Variablenzuweisung können spezielle Kommentare hinzugefügt werden, um den Typ eines Objekts anzugeben, wenn einer nicht direkt abgeleitet werden kann. Einfache Zuordnungen lassen sich im Allgemeinen leicht ableiten, andere, wie Listen (in Bezug auf ihren Inhalt), jedoch nicht.
Hinweis: Wenn wir eine Ableitung von verwenden möchten Containers
und den Inhalt für diesen Container angeben müssen , müssen wir die generischen Typen aus dem typing
Modul verwenden. Diese unterstützen die Indizierung.
# generic List, supports indexing.
from typing import List
# In this case, the type is easily inferred as type: int.
i = 0
# Even though the type can be inferred as of type list
# there is no way to know the contents of this list.
# By using type: List[str] we indicate we want to use a list of strings.
a = [] # type: List[str]
# Appending an int to our list
# is statically not correct.
a.append(i)
# Appending a string is fine.
a.append("i")
print(a) # [0, 'i']
Wenn wir diese Befehle zu einer Datei hinzufügen und sie mit unserem Interpreter ausführen, funktioniert alles einwandfrei und print(a)
druckt nur den Inhalt der Liste a
. Die # type
Kommentare wurden verworfen und als einfache Kommentare behandelt, die keine zusätzliche semantische Bedeutung haben .
Wenn mypy
wir dies mit ausführen, erhalten wir andererseits die folgende Antwort:
(Python3)jimmi@jim: mypy typeHintsCode.py
typesInline.py:14: error: Argument 1 to "append" of "list" has incompatible type "int"; expected "str"
Zeigt an, dass eine Liste von str
Objekten keine enthalten kann int
, was statisch gesehen ein Ton ist. Dies kann behoben werden, indem entweder der Typ der Objekte a
beibehalten und nur angehängt wird str
oder indem der Inhaltstyp von a
geändert wird, um anzuzeigen, dass ein beliebiger Wert akzeptabel ist (Intuitiv ausgeführt mit List[Any]
after Any
, aus dem importiert wurde typing
).
Funktionsanmerkungen werden im Formular param_name : type
nach jedem Parameter in Ihrer Funktionssignatur hinzugefügt, und ein Rückgabetyp wird unter Verwendung der -> type
Notation vor dem Endpunkt der Funktionsfunktion angegeben. Alle Anmerkungen werden im __annotations__
Attribut für diese Funktion in einer praktischen Wörterbuchform gespeichert . Verwenden eines einfachen Beispiels (für das keine zusätzlichen Typen aus dem typing
Modul erforderlich sind ):
def annotated(x: int, y: str) -> bool:
return x < y
Das annotated.__annotations__
Attribut hat jetzt die folgenden Werte:
{'y': <class 'str'>, 'return': <class 'bool'>, 'x': <class 'int'>}
Wenn wir ein absoluter Noobie sind oder mit Py2.7
Konzepten vertraut sind und daher das TypeError
Lauern im Vergleich von nicht kennen annotated
, können wir eine weitere statische Überprüfung durchführen, den Fehler abfangen und uns einige Probleme ersparen:
(Python3)jimmi@jim: mypy typeHintsCode.py
typeFunction.py: note: In function "annotated":
typeFunction.py:2: error: Unsupported operand types for > ("str" and "int")
Unter anderem wird der Aufruf der Funktion mit ungültigen Argumenten auch abgefangen:
annotated(20, 20)
# mypy complains:
typeHintsCode.py:4: error: Argument 2 to "annotated" has incompatible type "int"; expected "str"
Diese können grundsätzlich auf jeden Anwendungsfall ausgedehnt werden, und die festgestellten Fehler gehen weiter als grundlegende Aufrufe und Operationen. Die Typen, nach denen Sie suchen können, sind sehr flexibel, und ich habe nur einen kleinen Einblick in ihr Potenzial gegeben. Ein Blick in das typing
Modul, die PEPs oder die mypy
Dokumente gibt Ihnen einen umfassenderen Überblick über die angebotenen Funktionen.
Stub-Dateien:
Stub-Dateien können in zwei verschiedenen Fällen verwendet werden, die sich nicht gegenseitig ausschließen:
- Sie müssen check ein Modul eingeben, für das Sie die Funktionssignaturen nicht direkt ändern möchten
- Sie möchten Module schreiben und eine Typprüfung durchführen, aber zusätzlich Anmerkungen vom Inhalt trennen.
Was Stub-Dateien (mit einer Erweiterung von .pyi
) sind, ist eine kommentierte Schnittstelle des Moduls, das Sie erstellen / verwenden möchten. Sie enthalten die Signaturen der Funktionen, die Sie mit dem Hauptteil der verworfenen Funktionen überprüfen möchten. Um ein Gefühl dafür zu bekommen, geben Sie drei zufällige Funktionen in einem Modul mit dem Namen randfunc.py
:
def message(s):
print(s)
def alterContents(myIterable):
return [i for i in myIterable if i % 2 == 0]
def combine(messageFunc, itFunc):
messageFunc("Printing the Iterable")
a = alterContents(range(1, 20))
return set(a)
Wir können eine Stub-Datei erstellen randfunc.pyi
, in die wir einige Einschränkungen einfügen können, wenn wir dies wünschen. Der Nachteil ist, dass jemand, der die Quelle ohne den Stub betrachtet, diese Annotationshilfe nicht wirklich erhält, wenn er versucht zu verstehen, was wohin weitergegeben werden soll.
Wie auch immer, die Struktur einer Stub-Datei ist ziemlich simpel: Fügen Sie alle Funktionsdefinitionen mit leeren Körpern ( pass
gefüllt) hinzu und geben Sie die Anmerkungen entsprechend Ihren Anforderungen an. Nehmen wir hier an, wir möchten nur mit int
Typen für unsere Container arbeiten.
# Stub for randfucn.py
from typing import Iterable, List, Set, Callable
def message(s: str) -> None: pass
def alterContents(myIterable: Iterable[int])-> List[int]: pass
def combine(
messageFunc: Callable[[str], Any],
itFunc: Callable[[Iterable[int]], List[int]]
)-> Set[int]: pass
Die combine
Funktion gibt einen Hinweis darauf, warum Sie möglicherweise Anmerkungen in einer anderen Datei verwenden möchten. Manchmal wird der Code unübersichtlich und die Lesbarkeit verringert (großes Nein-Nein für Python). Sie könnten natürlich Typ-Aliase verwenden, aber das verwirrt manchmal mehr als es hilft (verwenden Sie sie also mit Bedacht).
Dies sollte Sie mit den Grundkonzepten von Type Hints in Python vertraut machen. Obwohl die verwendete Typprüfung bereits verwendet wurde,
mypy
sollten nach und nach weitere Popups angezeigt werden, einige intern in IDEs ( PyCharm ) und andere als Standard-Python-Module. Ich werde versuchen, zusätzliche Prüfer / verwandte Pakete in die folgende Liste aufzunehmen, wenn ich sie finde (oder wenn vorgeschlagen).
Dame, die ich kenne :
- Mypy : wie hier beschrieben.
- PyType : Verwendet von Google eine andere Notation als ich, wahrscheinlich einen Blick wert.
Verwandte Pakete / Projekte :
- typisiert: Offizielles Python-Repo mit einer Auswahl an Stub-Dateien für die Standardbibliothek.
Das typeshed
Projekt ist tatsächlich einer der besten Orte, an denen Sie nachsehen können, wie Typhinweise in einem eigenen Projekt verwendet werden können. Nehmen wir als Beispiel die __init__
Dunder der Counter
Klasse in der entsprechenden .pyi
Datei:
class Counter(Dict[_T, int], Generic[_T]):
@overload
def __init__(self) -> None: ...
@overload
def __init__(self, Mapping: Mapping[_T, int]) -> None: ...
@overload
def __init__(self, iterable: Iterable[_T]) -> None: ...
Wo _T = TypeVar('_T')
wird verwendet, um generische Klassen zu definieren . Für dieCounter
Klasse können wir sehen, dass sie entweder keine Argumente in ihrem Initialisierer annehmen, eine einzelne Mapping
von einem beliebigen Typ zu einer int
oder eine Iterable
von einem beliebigen Typ nehmen kann.
Beachten : Eine Sache, die ich vergessen habe zu erwähnen, war, dass das typing
Modul vorläufig eingeführt wurde . Aus PEP 411 :
Die API eines vorläufigen Pakets kann geändert werden, bevor ein "stabiler" Zustand erreicht wird. Einerseits bietet dieser Status dem Paket die Vorteile, formal Teil der Python-Distribution zu sein. Andererseits gibt das Kernentwicklungsteam ausdrücklich an, dass keine Zusagen hinsichtlich der Stabilität der API des Pakets gemacht werden, die sich für die nächste Version ändern könnten. Obwohl dies als unwahrscheinlich angesehen wird, können solche Pakete sogar ohne Verfallszeitraum aus der Standardbibliothek entfernt werden, wenn sich die Bedenken hinsichtlich ihrer API oder Wartung als begründet erweisen.
Also nimm die Dinge hier mit einer Prise Salz; Ich bin mir nicht sicher, ob es in bedeutender Weise entfernt oder verändert wird, aber man kann es nie erfahren.
** Ein weiteres Thema insgesamt, aber gültig im Rahmen von Typhinweisen:PEP 526
Syntax für Variablenanmerkungen ist ein Versuch, # type
Kommentare durch die Einführung einer neuen Syntax zu ersetzen , mit der Benutzer den Variablentyp in einfachen varname: type
Anweisungen mit Anmerkungen versehen können .
Siehe Was sind variable Anmerkungen in Python 3.6? , wie bereits erwähnt, für ein kleines Intro zu diesen.