Was sind Typhinweise in Python 3.5?

250

Eine der am häufigsten diskutierten Funktionen in Python 3.5 sind Typhinweise .

Ein Beispiel für Typ Hinweise wird in erwähnt diesen Artikel und diese während auch Typ Hinweise verantwortungsvoll nutzen erwähnen. Kann jemand mehr über sie erklären und wann sie verwendet werden sollten und wann nicht?

Vaulstein
quelle
4
Sie sollten sich PEP 484 ansehen, das aus dem offiziellen Changelog verlinkt ist .
Stefan
1
@ AvashashRaj: Eine gute Diskussion über die Veröffentlichungen findet hier statt
Vaulstein
1
Es ist schade, dass der C-API-Anwendungsfall von diesem PEP 484 vollständig ignoriert wird, insbesondere Typhinweise für Cython und Numba.
Denfromufa
2
Eng verwandt: Was sind variable Annotationen in Python 3.6? .
Dimitris Fasarakis Hilliard

Antworten:

343

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?

  1. 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.
  2. 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.
  3. 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 mypydie 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: typeKommentare, 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: typeKommentare)

Darüber hinaus möchten Sie Typhinweise in Verbindung mit dem in eingeführten neuen typingModul 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 ABCsin collections.abcsind enthalten, jedoch in einer GenericForm, um ein Abonnement zu ermöglichen (durch Definieren einer __getitem__()Methode).

Für alle, die an einer ausführlicheren Erklärung interessiert sind, mypy documentationist 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: typeWä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 Containersund den Inhalt für diesen Container angeben müssen , müssen wir die generischen Typen aus dem typingModul 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 # typeKommentare wurden verworfen und als einfache Kommentare behandelt, die keine zusätzliche semantische Bedeutung haben .

Wenn mypywir 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 strObjekten keine enthalten kann int, was statisch gesehen ein Ton ist. Dies kann behoben werden, indem entweder der Typ der Objekte abeibehalten und nur angehängt wird stroder indem der Inhaltstyp von ageä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 : typenach jedem Parameter in Ihrer Funktionssignatur hinzugefügt, und ein Rückgabetyp wird unter Verwendung der -> typeNotation 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 typingModul 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.7Konzepten vertraut sind und daher das TypeErrorLauern 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 typingModul, die PEPs oder die mypyDokumente 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 ( passgefüllt) hinzu und geben Sie die Anmerkungen entsprechend Ihren Anforderungen an. Nehmen wir hier an, wir möchten nur mit intTypen 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 combineFunktion 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, mypysollten 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 CounterKlasse in der entsprechenden .pyiDatei:

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 Mappingvon einem beliebigen Typ zu einer int oder eine Iterablevon einem beliebigen Typ nehmen kann.


Beachten : Eine Sache, die ich vergessen habe zu erwähnen, war, dass das typingModul 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, # typeKommentare durch die Einführung einer neuen Syntax zu ersetzen , mit der Benutzer den Variablentyp in einfachen varname: typeAnweisungen 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.

Dimitris Fasarakis Hilliard
quelle
3
"Aufgrund der hohen Dynamik von Python ist es besonders schwierig, den Typ eines verwendeten Objekts abzuleiten oder zu überprüfen." Sie beziehen sich auf statische Überprüfung, richtig?
bsam
53

Zu Jims ausführlicher Antwort hinzufügen:

Überprüfen Sie das typingModul - dieses Modul unterstützt Typhinweise gemäß PEP 484 .

Die folgende Funktion nimmt beispielsweise Werte vom Typ an strund gibt sie wie folgt zurück:

def greeting(name: str) -> str:
    return 'Hello ' + name

Das typingModul unterstützt außerdem:

  1. Geben Sie Aliasing ein .
  2. Geben Sie Hinweise für Rückruffunktionen ein .
  3. Generics - Abstrakte Basisklassen wurden erweitert, um Abonnements zu unterstützen und erwartete Typen für Containerelemente zu kennzeichnen.
  4. Benutzerdefinierte generische Typen - Eine benutzerdefinierte Klasse kann als generische Klasse definiert werden.
  5. Beliebiger Typ - Jeder Typ ist ein Untertyp von Beliebig.
Ani Menon
quelle
26

Das neu veröffentlichte PyCharm 5 unterstützt Typhinweise. In ihrem Blog-Beitrag darüber (siehe Python 3.5-Typhinweise in PyCharm 5 ) bieten sie eine großartige Erklärung, welche Typhinweise vorhanden sind und welche nicht sowie einige Beispiele und Abbildungen, wie Sie sie in Ihrem Code verwenden können.

Darüber hinaus wird es in Python 2.7 unterstützt, wie in diesem Kommentar erläutert :

PyCharm unterstützt das Typisierungsmodul von PyPI für Python 2.7, Python 3.2-3.4. Für 2.7 müssen Sie Typhinweise in * .pyi-Stub-Dateien einfügen, da in Python 3.0 Funktionsanmerkungen hinzugefügt wurden .

Tsvenson
quelle
0

Typhinweise sind eine neue Ergänzung zu einer dynamischen Sprache, in der die Leute jahrzehntelang so einfache Namenskonventionen wie Ungarisch schworen (Objektbezeichnung mit dem ersten Buchstaben b = Boolian, c = Zeichen, d = Wörterbuch, i = Ganzzahl, l = Liste, n = numerisch , s = string, t = tuple) wurden nicht benötigt, zu umständlich, aber jetzt haben wir entschieden, dass es viel zu schwierig ist, die Sprache (type ()) zum Erkennen von Objekten und unsere ausgefallenen IDEs zu verwenden Ich brauche Hilfe bei allem, was so kompliziert ist und dass dynamisch zugewiesene Objektwerte sie sowieso völlig unbrauchbar machen, während eine einfache Namenskonvention alles für jeden Entwickler auf einen Blick hätte lösen können.

Noah F. SanTsorvutz
quelle
Um ehrlich zu sein, klingt dies eher nach einem Schimpfen als nach einer Antwort.
Dimitris Fasarakis Hilliard
-1

Typhinweise dienen der Wartbarkeit und werden von Python nicht interpretiert. Im folgenden Code führt die Zeile def add(self, ic:int)erst in der nächsten return...Zeile zu einem Fehler :

class C1:
    def __init__(self):
        self.idn = 1
    def add(self, ic: int):
        return self.idn + ic
    
c1 = C1()
c1.add(2)

c1.add(c1)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "<input>", line 5, in add
TypeError: unsupported operand type(s) for +: 'int' and 'C1'
 
Leon Chang
quelle