Erkundung der Tiefen von Typanmerkungen in Python. Teil 1

Seit 2014, als Python die Unterstützung für Typanmerkungen einführte, arbeiten Programmierer an ihrer Implementierung in ihrem Code. Die Autorin des Materials, dessen erster Teil der Übersetzung wir heute veröffentlichen, sagt, dass nach ihrer Einschätzung in etwa 20 bis 30% des in Python 3 geschriebenen Codes ziemlich kühne, jetzt typisierte Anmerkungen (manchmal als „Hinweise“ bezeichnet) verwendet werden. Hier sind die Ergebnisse der Umfrage: was sie im Mai 2019 auf Twitter gepostet hat.

Wie sich herausstellte, werden Anmerkungen von 29% der Befragten verwendet. Laut der Autorin des Artikels ist sie in den letzten Jahren zunehmend auf Typanmerkungen in verschiedenen Büchern und Studienführern gestoßen.

Der zweite Teil



In der Python-Dokumentation werden die Begriffe "Typhinweis" und "Typanmerkung" synonym verwendet. Der Autor des Artikels verwendet hauptsächlich den Begriff "Typhinweis", wir verwenden den Begriff "Typanmerkung".

Dieser Artikel behandelt eine Vielzahl von Problemen im Zusammenhang mit der Verwendung von Typanmerkungen in Python. Wenn Sie den Originalartikel mit dem Autor besprechen möchten, können Sie den Pull-Request- Mechanismus verwenden.

Einführung


Hier finden Sie ein klassisches Beispiel dafür, wie Code aussieht, der mit Typanmerkungen geschrieben wurde.

Hier ist der reguläre Code:

def greeting(name):     return 'Hello ' + name 

Hier ist der Code, zu dem die Anmerkungen hinzugefügt werden:

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

Die Vorlage, nach der der Code mit Typanmerkungen ausgeführt wird, sieht folgendermaßen aus:

 def function(variable: input_type) -> return_type:    pass 

Auf den ersten Blick scheint das Anwenden von Typanmerkungen im Code einfach und unkompliziert zu sein. In der Entwicklergemeinde besteht jedoch immer noch ein gewisses Maß an Unsicherheit darüber, was genau Typanmerkungen sind. Darüber hinaus gibt es sogar Unklarheiten darüber, wie man sie richtig aufruft - "Anmerkungen" oder "Hinweise" - und welche Vorteile ihre Verwendung in der Codebasis bietet.

Als ich anfing, dieses Thema zu untersuchen und darüber nachzudenken, ob ich Typanmerkungen verwenden muss, war ich völlig verwirrt. Infolgedessen beschloss ich, das Gleiche wie immer zu tun, nachdem ich auf etwas Unverständliches gestoßen war. Ich habe mich entschlossen, dieses Problem gründlich zu untersuchen und meine Recherchen in Form dieses Materials darzulegen, das hoffentlich für diejenigen nützlich sein wird, die sich wie ich mit Typanmerkungen in Python befassen möchten.

Wie führen Computer unsere Programme aus?


Um zu verstehen, was Python-Entwickler mit Typanmerkungen erreichen möchten, sprechen wir über die Mechanismen von Computersystemen, die sich mehrere Ebenen unterhalb des Python-Codes befinden. Dank dessen können wir besser verstehen, wie Computer und Programmiersprachen im Allgemeinen funktionieren.

Programmiersprachen sind im Kern Tools, mit denen Sie Daten mit einer Zentraleinheit (CPU) verarbeiten sowie die zu verarbeitenden Daten und die aus der Verarbeitung resultierenden Daten im Speicher speichern können.


Vereinfachte Computerschaltung

Ein Prozessor ist im Wesentlichen eine ziemlich dumme Sache. Er ist in der Lage, beeindruckende Aktionen mit Daten auszuführen, versteht jedoch nur Maschinenanweisungen, die sich auf elektrische Signale beschränken. Die Maschinensprache kann so dargestellt werden, dass sie aus Nullen und Einsen besteht.
Um diese Nullen und Einsen vorzubereiten, die der Prozessor versteht, müssen Sie den Code von einer Hochsprache in eine Niedrigsprache übersetzen. Hier kommen Compiler und Interpreten ins Spiel.

Wenn die Sprache kompiliert oder ausführbar ist (Python-Code wird mithilfe des Interpreters ausgeführt), wird der Code in dieser Sprache zu Maschinencode auf niedriger Ebene, der Anweisungen für Computerkomponenten auf niedriger Ebene, dh für Hardware, enthält.

Es gibt verschiedene Möglichkeiten, in einer Programmiersprache geschriebenen Code in Code zu übersetzen, den Maschinen verstehen können. Sie können entweder eine Datei mit dem Programmcode erstellen und diese mit dem Compiler in Maschinencode konvertieren (so funktionieren C ++, Go, Rust und einige andere Sprachen) oder den Code direkt mit dem Interpreter ausführen, der für die Konvertierung des Codes in Maschinenbefehle verantwortlich ist. Auf diese Weise werden mit Hilfe von Dolmetschern Programme in Python sowie in anderen "Skriptsprachen" wie PHP und Ruby gestartet.


Verarbeitetes Sprachcode-Verarbeitungsschema

Woher weiß die Hardware, wie Nullen und Einsen, die die Daten darstellen, mit denen das Programm arbeitet, im Speicher gespeichert werden? Unser Programm muss den Computer darüber informieren, wie Speicher für diese Daten zugewiesen werden soll. Und was sind diese Daten? Dies hängt davon ab, welche Datentypen eine bestimmte Sprache unterstützt.

Datentypen sind in allen Sprachen verfügbar. In der Regel sind Datentypen eines der ersten Themen, mit denen Anfänger das Programmieren in einer bestimmten Sprache lernen.

Es gibt zum Beispiel ausgezeichnete Tutorials zu demselben Python - hier finden Sie detaillierte Informationen zu Datentypen. Mit einfachen Worten, Datentypen sind verschiedene Arten der Darstellung von im Speicher gespeicherten Daten.

Unter den vorhandenen Datentypen können beispielsweise Zeichenfolgen und Ganzzahlen notiert werden. Der Datentyp, der dem Entwickler zur Verfügung steht, hängt von der verwendeten Programmiersprache ab. Hier ist zum Beispiel eine Liste grundlegender Python-Datentypen :

 int, float, complex str bytes tuple frozenset bool array bytearray list set dict 

Es gibt Datentypen, die aus anderen Datentypen bestehen. Beispielsweise kann eine Liste in Python Ganzzahlen oder Zeichenfolgen sowie beides speichern.

Um herauszufinden, wie viel Speicher Sie zum Speichern einiger Daten zuweisen müssen, muss der Computer wissen, welche Art von Daten das Programm im Speicher ablegen wird. Python verfügt über eine getsizeof Funktion getsizeof , die uns über die in Bytes ausgedrückte Speichermenge informiert, die zum Speichern der Werte verschiedener Datentypen erforderlich ist.

Hier ist eine großartige Antwort auf StackOverflow, in der Sie Informationen dazu finden, wie Sie die Größe der „Mindestwerte“ ermitteln können, die in verschiedenen Arten von Variablen gespeichert werden können.

 import sys import decimal import operator d = {"int": 0,    "float": 0.0,    "dict": dict(),    "set": set(),    "tuple": tuple(),    "list": list(),    "str": "a",    "unicode": u"a",    "decimal": decimal.Decimal(0),    "object": object(), } #   ,        d_size = {} for k, v in sorted(d.items()):    d_size[k]=sys.getsizeof(v) sorted_x = sorted(d_size.items(), key=lambda kv: kv[1]) sorted_x [('object', 16), ('float', 24), ('int', 24), ('tuple', 48), ('str', 50), ('unicode', 50), ('list', 64), ('decimal', 104), ('set', 224), ('dict', 240)] 

Wenn wir ein Wörterbuch sortieren, das Stichproben von Werten verschiedener Typen enthält, können wir feststellen, dass die maximale Größe ein leeres Wörterbuch ( dict ) und gefolgt von einer Menge ( set ) ist. Im Vergleich zu ihnen wird sehr wenig Speicherplatz benötigt, um eine Ganzzahl (Typ int ) zu speichern.

Das obige Beispiel gibt uns eine Vorstellung davon, wie viel Speicher erforderlich ist, um verschiedene in Programmen verwendete Werte zu speichern.

Warum sollte uns das überhaupt stören? Tatsache ist, dass einige Typen besser als andere sind, um einige Probleme zu lösen, sodass Sie diese Probleme effektiver lösen können. In einigen Situationen müssen die Typen sorgfältig geprüft werden. Beispielsweise wird manchmal überprüft, ob die im Programm verwendeten Datentypen nicht im Widerspruch zu einigen Annahmen stehen, die beim Entwerfen des Programms getroffen wurden.

Aber was sind diese Typen? Warum brauchen wir sie? Hier kommt das Konzept des „Typensystems“ ins Spiel.

Einführung in Typsysteme


Vor langer Zeit haben Menschen, die mathematische Berechnungen manuell durchgeführt haben , in einer fernen Galaxie erkannt, dass sie beim Vergleich der „Typen“ mit Zahlen oder Elementen von Gleichungen die Anzahl der logischen Fehler reduzieren können, die beim Ableiten mathematischer Beweise für diese Elemente auftreten.

Da die Informatik zu Beginn im Wesentlichen auf die Durchführung großer Mengen manueller Berechnungen beschränkt war, wurden einige dieser alten Prinzipien auf diese Berechnungen übertragen. Typsysteme sind zu einem Werkzeug geworden, mit dem die Anzahl der Fehler in Programmen verringert werden kann, indem verschiedenen Variablen oder Elementen geeignete Typen zugewiesen werden.

Hier einige Beispiele:

  • Wenn wir Software für die Bank schreiben, können wir die Zeilen im Codefragment nicht verwenden, mit denen der Kontostand auf dem Konto eines anderen berechnet wird.
  • Wenn wir mit den Daten einer Umfrage arbeiten und verstehen möchten, ob jemand eine Frage positiv oder negativ beantwortet hat, werden die Antworten „Ja“ und „Nein“ ganz natürlich mit einem logischen Typ codiert.
  • Bei der Entwicklung einer großen Suchmaschine müssen wir die Anzahl der Zeichen begrenzen, die Benutzer dieses Systems in das Suchabfragefeld eingeben können. Dies bedeutet, dass wir einige Zeichenfolgendaten auf Übereinstimmung mit bestimmten Parametern überprüfen müssen.

In der heutigen Programmierung gibt es zwei Haupttypsysteme. Dazu schreibt Steve Klabnik : „Ein statisches Typsystem ist der Mechanismus, mit dem der Compiler den Quellcode überprüft und Programmfragmenten Labels (sogenannte„ Typen “) zuweist und daraus Schlussfolgerungen über das Programmverhalten zieht. Ein dynamisches Typsystem ist der Mechanismus, mit dem der Compiler Code generiert, um zu überwachen, welche Datentypen (die zufällig auch als "Typen" bezeichnet werden) vom Programm verwendet werden. "

Was bedeutet das? Dies bedeutet, dass Sie bei der Arbeit mit kompilierten Sprachen normalerweise Entitätstypen im Voraus zuweisen müssen. Dank dessen kann der Compiler sie während der Kompilierung des Codes überprüfen und herausfinden, ob es möglich ist, aus dem ihm zur Verfügung gestellten Quellcode ein aussagekräftiges Programm zu erstellen.

Ich bin kürzlich auf eine Erklärung für den Unterschied zwischen statischer und dynamischer Typisierung gestoßen. Dies ist wahrscheinlich der beste Text, den ich zu diesem Thema gelesen habe. Hier ein Fragment davon: „Früher habe ich statisch typisierte Sprachen verwendet, aber in den letzten Jahren habe ich hauptsächlich in Python programmiert. Anfangs ärgerte mich die statische Eingabe ein wenig. Ich hatte das Gefühl, dass die Notwendigkeit, Variablentypen zu deklarieren, langsamer wird und mich dazu zwingt, meine Ideen übermäßig auszudrücken. Python ließ mich einfach tun, was ich wollte, auch wenn ich versehentlich etwas falsch gemacht habe. Die Verwendung von Sprachen mit statischer Typisierung ist so, als würde man jemandem eine Aufgabe geben, der immer wieder fragt und die kleinen Details des Falls klärt, den er abschließen soll. Dynamisches Tippen ist, wenn die Person, die die Aufgabe erhält, immer zustimmend nickt. In diesem Fall hat er das Gefühl, Sie verstanden zu haben. Aber manchmal gibt es keine vollständige Gewissheit, dass derjenige, dem die Aufgabe übertragen wurde, richtig herausgefunden hat, was von ihm erwartet wird. “

Als ich über Typsysteme sprach, stieß ich auf etwas, das ich nicht sofort verstand. Die Konzepte "statische Typisierung" und "dynamische Typisierung" sind nämlich eng mit den Konzepten "kompilierte Sprache" und "interpretierte Sprache" verwandt, aber die Begriffe "statisch" und "kompiliert" sowie die Begriffe "dynamisch" und "interpretiert" sind keine Synonyme . Die Sprache kann wie Python dynamisch eingegeben und gleichzeitig kompiliert werden. Ebenso kann eine Sprache statisch typisiert werden, z. B. Java, aber auch interpretiert werden (z. B. im Fall von Java bei Verwendung von Java REPL).

Vergleich von Datentypen in statisch und dynamisch typisierten Sprachen


Was ist der Unterschied zwischen Datentypen in statisch und dynamisch typisierten Sprachen?

Bei Verwendung der statischen Typisierung müssen Typen im Voraus deklariert werden. Wenn Sie beispielsweise in Java arbeiten, sehen Ihre Programme ungefähr so ​​aus:

 public class CreatingVariables {  public static void main(String[] args) {    int x, y, age, height;    double seconds, rainfall;    x = 10;    y = 400;    age = 39;    height = 63;    seconds = 4.71;    rainfall = 23;    double rate = calculateRainfallRate(seconds, rainfall);   } private static double calculateRainfallRate(double seconds, double rainfall) {  return rainfall/seconds; } 

Achten Sie auf den Beginn des Programms. Dort werden mehrere Variablen deklariert, neben denen Angaben zu den Typen dieser Variablen stehen:

 int x, y, age, height; double seconds, rainfall; 

Darüber hinaus werden Typen sowohl beim Deklarieren von Funktionen als auch beim Deklarieren ihrer Argumente angegeben. Ohne diese Typdeklarationen kann das Programm nicht kompiliert werden. Wenn Sie Java-Programme erstellen, müssen Sie von Anfang an planen, welche Typen diese oder jene Entitäten haben werden. Infolgedessen weiß der Compiler während der Verarbeitung des Codes solcher Programme, was genau er benötigt, um beim Generieren von Maschinencode zu prüfen.

Python entlastet einen Programmierer. Ein ähnlicher Python-Code könnte folgendermaßen aussehen:

 y = 400 age = 39 height = 63 seconds = 4.71 rainfall = 23 rate = calculateRainfall(seconds, rainfall) def calculateRainfall(seconds, rainfall):  return rainfall/seconds 

Wie funktioniert das alles im Darm von Python? Fortsetzung folgt…

Liebe Leser! Welche Programmiersprache, die Sie verwendet haben, hat den angenehmsten Eindruck hinterlassen?

Source: https://habr.com/ru/post/de463929/


All Articles