Für alle Habrach-Bewohner, die das Gefühl von Deja Vu haben: Ich wurde aufgefordert, diesen Beitrag durch den Artikel "Einführung in Python" und Kommentare dazu zu schreiben. Leider ist die Qualität dieser "Einführung" ähm ... lassen Sie uns nicht über traurige Dinge sprechen. Es war jedoch noch trauriger, Streitigkeiten in den Kommentaren aus den Kategorien „C ++ ist schneller als Python“, „Rost ist noch schneller als C ++“, „Python wird nicht benötigt“ usw. zu beobachten. Es ist erstaunlich, dass sie sich nicht an Ruby erinnerten!
Wie Bjarn Stroustrup sagte,
"Es gibt nur zwei Arten von Programmiersprachen: die, auf die die Leute ständig schwören, und die, die niemand benutzt."
Willkommen bei kat allen, die Python kennenlernen möchten, ohne sich auf schmutzige Flüche einzulassen!
Der Morgen in den Bergen des Ostkaukasus war von Schreien geprägt. Zwei junge Männer saßen auf einem großen Felsbrocken und diskutierten heftig über etwas, gestikulierten aktiv. Eine Minute später fingen sie an, sich gegenseitig zu schieben, griffen dann und fielen von einem Felsbrocken in einen Brennnesselbusch (wie sich herausstellte). Anscheinend wuchs dieser Busch aus einem bestimmten Grund dort - er beruhigte die Schläger sofort und brachte einen Waffenstillstand zu ihrem unstillbaren Streit. Wie Sie wahrscheinlich vermutet haben, war ich einer der Debattierer, der andere war mein bester Freund (Hallo, Quaker_t!), Aber das Thema unseres Small Talks war Visual Basic vs. Delphi !
Erkennst du dich selbst? Manchmal verwandeln wir unsere Lieblingsprogrammiersprachen in einen Kult und sind bereit, ihn bis zuletzt zu verteidigen! Aber die Jahre vergehen und der Moment kommt, in dem "A gegen B" aus dem Thema Streitigkeiten zu "Ich fühle mich wohler mit A, aber wenn nötig werde ich lernen, mit B, C, D, E und im Allgemeinen mit irgendetwas zu arbeiten ." Nur wenn wir auf neue Programmiersprachen stoßen, lassen uns alte Gewohnheiten und Kultur möglicherweise nicht lange los.
Ich möchte Ihnen Python vorstellen und Ihnen helfen, Ihre Erfahrungen in eine neue Richtung zu lenken. Wie jede Technologie hat sie ihre eigenen Stärken und Schwächen. Python ist wie C ++, Rust, Ruby, JS und alle anderen ein Werkzeug. Anweisungen sind jedem Instrument beigefügt, und Sie müssen lernen, jedes Instrument richtig zu verwenden .
"Autor, hast du kein Gehirn, wolltest du uns Python vorstellen?" Lass uns kennenlernen!
Python ist eine dynamische Allzweck-Programmiersprache auf hoher Ebene. Python ist eine ausgereifte Programmiersprache mit einem reichen Ökosystem und einer reichen Tradition. Obwohl die Sprache 1991 veröffentlicht wurde, nahm ihr modernes Erscheinungsbild Anfang der 2000er Jahre Gestalt an. Python ist eine aufgeladene Sprache. In der Standardbibliothek gibt es Lösungen für viele Gelegenheiten. Python ist eine beliebte Programmiersprache: Dropbox, Reddit, Instagram, Disqus, YouTube, Netflix, verdammt noch mal, sogar Eve Online und viele andere nutzen Python aktiv.
Was ist der Grund für diese Popularität? Mit Ihrer Erlaubnis werde ich meine eigene Version präsentieren.
Python ist eine einfache Programmiersprache. Dynamische Eingabe. Müllsammler. Funktionen höherer Ordnung. Einfache Syntax für die Arbeit mit Wörterbüchern, Mengen, Tupeln und Listen (auch zum Abrufen von Slices). Python ist ideal für Anfänger: Es ermöglicht es, mit der prozeduralen Programmierung zu beginnen, langsam zu OOP zu wechseln und einen Eindruck von der funktionalen Programmierung zu bekommen. Aber diese Einfachheit ist wie die Spitze eines Eisbergs. Es lohnt sich, in die Tiefe zu tauchen, wenn Sie auf Pythons Philosophie stoßen - Zen Of Python . Tauchen Sie noch weiter - und Sie erhalten eine Reihe klarer Regeln für die Gestaltung des Codes - Style Guide für Python-Code . Beim Tauchen vertieft sich der Programmierer allmählich in das Konzept von "Python Way" oder "Pythonic". In dieser erstaunlichen Phase des Sprachenlernens beginnen Sie zu verstehen, warum gute Python-Programme so und nicht anders geschrieben sind. Warum sich die Sprache in diese Richtung entwickelt hat und nicht in eine andere. Python war in der Geschwindigkeit nicht erfolgreich. Aber er hat den wichtigsten Aspekt unserer Arbeit erreicht - die Lesbarkeit. "Schreiben Sie Code für Menschen, nicht für Autos" - dies ist die Grundlage für die Grundlagen von Python.
Guter Python-Code sieht wunderschön aus. Und schönen Code zu schreiben - was ist kein angenehmer Beruf?
Tipp 0: Bevor Sie weiterlesen, schauen Sie sich bitte eine Zen Python- Ecke an. Die Sprache basiert auf diesen Postulaten und unsere Kommunikation wird viel angenehmer, wenn Sie mit ihnen vertraut sind.
Welcher kluge Mann hat sich Einrückungen ausgedacht?
Der erste Schock für diejenigen, die den Code in Python noch nie gesehen haben, ist das Einrücken der Anweisungen:
def main(): ins = input('Please say something') for w in ins.split(' '): if w == 'hello': print('world!')
Ich erinnere mich an die Abende im Hostel des St. Petersburg Polytechnic, als mein Nachbar VlK mir mit brennenden Augen erzählte, dass er in Python etwas Neues entdeckt hatte. "Einrückungskörper? Ernsthaft?" - War meine Reaktion. Für eine Person, die über C, C ++ und Java von Visual Basic ( if ... end if
) zu C # (geschweifte Klammern) überging, schien dieser Ansatz, gelinde gesagt, seltsam. "Formatieren Sie den Code mit Einrückung?", Fragte VlK . Natürlich habe ich es formatiert. Genauer gesagt hat Spiral Studio Visual das für mich getan. Sie hat es verdammt gut gemacht. Ich habe nie über Formatierung und Einrückung nachgedacht - sie erschienen selbst im Code und schienen etwas Gewöhnliches und Vertrautes zu sein. Aber es gab nichts zu verbergen - der Code war immer mit Einrückungen formatiert. "Warum brauchen Sie dann geschweifte Klammern, wenn der Anweisungskörper auf jeden Fall nach rechts verschoben ist?"
In dieser Nacht setzte ich mich mit Python zusammen. Rückblickend kann ich mit Sicherheit sagen, was genau dazu beigetragen hat, schnell neues Material aufzunehmen. Es war ein Code-Editor. Unter dem Einfluss des gleichen VlK wechselte ich kurz vor den oben beschriebenen Ereignissen als Editor von Windows zu Ubuntu und Emacs (im Innenhof von 2007 zu PyCharm, Atom, VS Code und anderen - viele weitere Jahre). "Nun, jetzt wird Emacs PR ..." - sagen Sie. Nur ein bisschen :) Traditionell fügt die <tab>
-Taste in Emacs keine Registerkarten hinzu, sondern dient dazu, die Linie gemäß den Regeln dieses Modus auszurichten. Drücken Sie die <tab>
- und die Codezeile wird an die nächste geeignete Position verschoben:

Auf diese Weise müssen Sie nie darüber nachdenken, ob Sie den Code richtig ausgerichtet haben.
Tipp 1: Wenn Sie Python kennenlernen, verwenden Sie einen Editor, der sich um die Einrückung kümmert.
Wissen Sie, welche Nebenwirkung all diese Schande hat? Der Programmierer versucht, lange Konstruktionen zu vermeiden. Sobald die Größe der Funktion die vertikalen Ränder des Bildschirms überschreitet, wird es schwieriger zu unterscheiden, zu welchem Design der angegebene Codeblock gehört. Und je mehr Investitionen, desto schwieriger. Infolgedessen versuchen Sie, so präzise wie möglich zu schreiben und die langen Funktionskörper, Schleifen, bedingten Übergänge usw. aufzubrechen.
Na ja, deine dynamische Eingabe
O, diese Diskussion existiert fast so lange, wie das Konzept der "Programmierung" existiert! Dynamisches Tippen ist weder schlecht noch gut. Dynamisches Tippen ist auch unser Werkzeug. In Python bietet dynamisches Tippen enorme Handlungsfreiheit. Und wo es mehr Handlungsfreiheit gibt - eher, sich in den Fuß zu schießen.
Es sollte klargestellt werden, dass die Eingabe in Python streng ist und das Hinzufügen einer Zahl zu einer Zeichenfolge nicht funktioniert:
1 + '1' >>> TypeError: unsupported operand type(s) for +: 'int' and 'str'
Python überprüft auch die Signatur der Funktion, wenn sie aufgerufen wird, und löst eine Ausnahme aus, wenn die Aufrufsignatur nicht wahr ist:
def sum(x, y): return x + y sum(10, 20, 30) >>> TypeError: sum() takes 2 positional arguments but 3 were given
Beim Laden eines Skripts teilt Python Ihnen jedoch nicht mit, dass die Funktion eine Zahl und keine Zeichenfolge erwartet, die Sie an das Skript übergeben. Und das erfahren Sie erst zur Laufzeit:
def sum(x, y): return x + y sum(10, '10') >>> TypeError: can only concatenate str (not "int") to str
Je größer die Herausforderung für den Programmierer, insbesondere beim Schreiben großer Projekte . Modern Python hat diese Herausforderung mit einer Annotation Engine und einer Typbibliothek beantwortet, und die Community hat Programme entwickelt, die statische Typprüfungen durchführen . Infolgedessen erfährt der Programmierer von solchen Fehlern, bevor er das Programm ausführt:
Python misst Anmerkungen keine Bedeutung bei, obwohl es sie im Attribut __annotations__
speichert. Die einzige Bedingung ist, dass Anmerkungen sprachlich gültige Werte sein müssen. Seit ihrem Erscheinen in Version 3.0 (vor mehr als zehn Jahren!) Begannen die Bemühungen der Community, Anmerkungen für die typisierte Markierung von Variablen und Argumenten zu verwenden.
Ein weiteres Beispiel, komplizierter. Tipp 2: In der Praxis verursacht die dynamischste Eingabe Probleme beim Lesen und Debuggen von Code. Vor allem, wenn dieser Code ohne Anmerkungen geschrieben wurde und Sie viel Zeit damit verbringen müssen, die Variablentypen herauszufinden. Sie müssen nicht die Arten von allem und allem angeben und dokumentieren, aber die Zeit, die für eine detaillierte Beschreibung der öffentlichen Schnittstellen und der kritischsten Codeabschnitte aufgewendet wird, wird hundertfach belohnt!
Quacksalber! Ente tippen
Manchmal geben Python-Enthusiasten vor, mysteriös zu sein und sprechen über "Duck Typing".
Das Tippen von Enten ist die Verwendung des Ententests bei der Programmierung:
Wenn ein Objekt wie eine Ente quakt, wie eine Ente fliegt und wie eine Ente geht, dann ist es höchstwahrscheinlich eine Ente.
Betrachten Sie ein Beispiel:
class RpgCharacter: def __init__(self, weapon) self.weapon = weapon def battle(self): self.weapon.attack()
Hier ist die klassische Abhängigkeitsinjektion. Die RpgCharacter
Klasse empfängt das RpgCharacter
im Konstruktor und ruft später in der battle()
-Methode weapon.attack()
. RpgCharacter
hängt jedoch nicht von der spezifischen Implementierung der weapon
. Es kann ein Schwert, ein BFG 9000 oder ein Wal mit einem Blumentopf sein, der jederzeit bereit ist, auf dem Kopf des Feindes zu landen. Es ist wichtig, dass das Objekt eine attack()
-Methode hat. Python interessiert sich nicht für alles andere.
Genau genommen ist das Tippen von Enten kein Einzelfall. Es ist in allen (mir vertrauten) dynamischen Sprachen vorhanden, die OOP implementieren.
Dies ist ein weiteres Beispiel dafür, wie man in der Welt des dynamischen Schreibens sorgfältig programmiert. Schlecht benannte Methode? Mehrdeutig eine Variable benannt? Ihr Kollege oder Sie selbst werden nach etwa einem halben Jahr gerne einen solchen Code erstellen :)
Was würde passieren, wenn wir bedingtes Java verwenden? interface IWeapon { void attack(); } public class Sword implements IWeapon { public void attack() {
Und es würde eine klassische statische Typisierung geben, bei der die Typprüfung in der Kompilierungsphase erfolgt. Preis - Die Unfähigkeit, ein Objekt zu verwenden, das über eine attack()
-Methode verfügt, die IWeapon
Schnittstelle jedoch nicht explizit implementiert.
Tipp 3 : Wenn Sie möchten, können Sie die Schnittstelle beschreiben, indem Sie eine eigene abstrakte Klasse mit Methoden und Eigenschaften erstellen. Besser noch, verbringen Sie Zeit damit, die Dokumentation für sich und Ihre Codebenutzer gründlich zu testen und zu schreiben.
Verfahrensansatz und __ spezielle_ Methoden __ ()
Python ist eine objektorientierte Sprache und die object
ist die Wurzel der Vererbungshierarchie:
isinstance('abc', object) >>> True isinstance(10, object) >>> True
obj.ToString()
in Java und C # verwendet wird, wird in Python str(obj)
. Oder anstelle von myList.length
hat Python len(my_list)
. Der Schöpfer der Sprache, Guido van Rossum, erklärte dies wie folgt:
Wenn ich den Code mit der Aufschrift len(x)
lese, weiß ich, dass die Länge von etwas angefordert wird. Dies sagt mir sofort, dass das Ergebnis eine Ganzzahl ist und das Argument eine Art Container ist. Umgekehrt muss ich beim Lesen von x.len()
wissen, dass x
eine Art Container ist, der eine bestimmte Schnittstelle implementiert oder von einer Klasse mit der len()
-Methode erbt. [Quelle] .
Trotzdem rufen die Funktionen len()
, str()
und einige andere in sich bestimmte Methoden des Objekts auf:
class User: def __init__(self, name, last_name): self.name = name self.last_name = last_name def __str__(self): return f"Honourable {self.name} {self.last_name}" u = User('Alex', 'Black') label = str(u) print(label) >>> Honourable Alex Black
Spezielle Methoden werden auch von mathematischen und booleschen Sprachoperatoren sowie for ... in ...
Schleifenoperatoren with
Kontextoperator, Indexoperator []
usw. verwendet.
Ein Iteratorprotokoll besteht beispielsweise aus zwei Methoden: __iter__()
und __next__()
:
Sagen wir spezielle Methoden. Aber warum sehen sie so verdreht aus? Guido erklärte dies damit, dass sie die üblichen Namen hatten, ohne sie zu unterstreichen. Die Programmierer selbst würden sie zumindest früher oder später nicht neu definieren. Das heißt, ____()
ist eine Art Schutz gegen den Narren. Wie die Zeit gezeigt hat - Schutz ist wirksam :)
Tipp 4: Sehen Sie sich die integrierten Funktionen und speziellen Objektmethoden genau an. Sie sind ein wesentlicher Bestandteil der Sprache, ohne die es unmöglich ist, sie vollständig zu sprechen.
Wo ist die Kapselung? Wo ist mein privates ?! Wo ist mein Märchen? !!
Python hat keine Zugriffsmodifikatoren für Klassenattribute. Die Innenräume von Objekten sind uneingeschränkt zugänglich. Es gibt jedoch eine Konvention, nach der Attribute mit dem Präfix _
als privat betrachtet werden, zum Beispiel:
import os class MyFile:
Warum?
In Python gibt es nichts Privates. Weder die Klasse noch ihre Instanz werden vor Ihnen verbergen, was sich darin befindet (dank dessen die tiefste Selbstbeobachtung möglich ist). Python vertraut dir. Er sagt sozusagen: „Kumpel, wenn du in dunklen Ecken herumwühlen willst - kein Problem. Ich glaube, dass es gute Gründe dafür gibt und ich hoffe, dass du nichts kaputt machst.
Am Ende sind wir alle Erwachsene hier.
- Karl Fast [Quelle] .
Aber wie vermeide ich Namenskollisionen während der Vererbung?Python hat einen speziellen Mechanismus, um den Namen von Attributen zu __my_attr
mit einem doppelten Unterstrich beginnen und nicht mit einem doppelten Unterstrich enden ( __my_attr
)! Dies geschieht, um Namenskollisionen während der Vererbung zu vermeiden. Um die Klassenmethoden außerhalb des Körpers ___
, fügt Python das Präfix ___
hinzu. Für den internen Zugriff ändert sich jedoch nichts:
class C: def __init__(self): self.__x = 10 def get_x(self): return self.__x c = C() c.__x >>> 'C' object has no attribute '__x' print(c.get_x()) >>> 10 print(c._C__x) >>> 10
Schauen wir uns eine praktische Anwendung an. Beispielsweise möchten wir der File
Klasse, die Dateien aus dem lokalen Dateisystem liest, Caching-Funktionen hinzufügen. Unser Kollege hat es geschafft, eine Mixin-Klasse für diese Zwecke zu schreiben. Um Methoden und Attribute von potenziellen Konflikten zu isolieren, fügte ein Kollege seinen Namen das Präfix __
hinzu:
class BaseFile: def __init__(self, path): self.path = path class LocalMixin: def read_from_local(self): with open(self.path) as f: return f.read() class CachedMixin: class CacheMissError(Exception): pass def __init__(self):
Wenn Sie sich für die Implementierung dieses Mechanismus in CPython interessieren, lesen Sie bitte Python / compile.c
Aufgrund des Vorhandenseins von Eigenschaften in der Sprache ist es schließlich nicht sinnvoll, Getter und Setter im Java-Stil zu schreiben: getX(), setX()
. Zum Beispiel in den ursprünglich geschriebenen Klassenkoordinaten,
class Coordinates: def __init__(self, x, y): self.x = x self.y = y c = Coordinates(10, 10) print(cx, cy) >>> (10, 10)
Ich musste den Zugriff auf das x
Attribut steuern. Der richtige Ansatz wäre, es durch property
zu ersetzen und dadurch einen Vertrag mit der Außenwelt aufrechtzuerhalten.
class Coordinates: _x = 0 def __init__(self, x, y): self.x = x self.y = y @property def x(self): return self._x @x.setter def x(self, val): if val > 10: self._x = val else: raise ValueError('x should be greater than 10') c = Coordinates(20, 10) cx = 5 >>> ValueError: x should be greater than 10
Tipp 5: Wie so viel in Python basiert das Konzept der privaten Felder und Klassenmethoden auf einer etablierten Konvention. Lassen Sie sich von den Autoren von Bibliotheken nicht beleidigen, wenn "alles nicht mehr funktioniert", weil Sie die privaten Felder ihrer Klassen aktiv genutzt haben. Am Ende sind wir alle Erwachsene hier :) .
Ein bisschen über Ausnahmen
Die Python-Kultur hat einen einzigartigen Ansatz für Ausnahmen. Zusätzlich zum üblichen Abfangen und Verarbeiten von a la C ++ / Java werden im Kontext Ausnahmen verwendet
"Einfacher um Vergebung zu bitten als um Erlaubnis - EAFP."
Um es zu paraphrasieren: Schreiben Sie nicht zu viel, if
in den meisten Fällen die Ausführung in diesem Zweig erfolgt. Wickeln Sie stattdessen die Logik in try..except
.
Beispiel: Stellen Sie sich einen POST-Anforderungshandler vor, der einen Benutzer in einer bedingten Datenbank erstellt. Am Eingang der Funktion befindet sich ein Wörterbuch (Wörterbuch) vom Typ Schlüsselwert:
def create_user_handler(data: Dict[str, str]): try: database.user.persist( username=data['username'], password=data['password'] ) except KeyError: print('There was a missing field in data passed for user creation')
Wir haben den Code nicht mit Überprüfungen "ob username
oder password
in data
enthalten data
" verschmutzt. Wir erwarten, dass sie höchstwahrscheinlich dort sein werden. Wir bitten nicht um "Erlaubnis", diese Felder zu verwenden, sondern "entschuldigen", wenn der nächste Kulhacker ein Formular mit fehlenden Daten veröffentlicht.
Bring es einfach nicht auf den Punkt der Absurdität!Sie möchten beispielsweise überprüfen, ob der Nachname des Benutzers in den Daten vorhanden ist, und ihn nicht auf einen leeren Wert setzen. if
hier viel angemessener wäre:
def create_user_handler(data): if 'last_name' not in data: data['last_name'] = '' try: database.user.persist( username=data['username'], password=data['password'], last_name=data['last_name'] ) except KeyError: print('There was a missing field in data passed for user creation')
Fehler sollten niemals stillschweigend vergehen. - Ignorieren Sie die Ausnahmen nicht! Modern Python hat eine wunderbare raise from
Konstrukts, mit der Sie den Kontext der Ausnahmekette beibehalten können. Zum Beispiel:
class MyProductError(Exception): def __init__(self): super().__init__('There has been a terrible product error') def calculate(x): try: return 10 / x except ZeroDivisionError as e: raise MyProductError() from e
Ohne MyProductError
raise from e
Ausnahmekette in MyProductError
, und wir können nicht herausfinden, was genau die Ursache für diesen Fehler war. Bei der raise from X
wird der Grund (d. H. X
) der ausgelösten Ausnahme im Attribut __cause__
gespeichert:
try: calculate(0) except MyProductError as e: print(e.__cause__) >>> division by zero
Bei der Iteration gibt es jedoch eine kleine Nuance: StopIterationIm Falle einer Iteration ist das Auslösen einer StopIteration- Ausnahme die offizielle Methode, um zu signalisieren, dass der Iterator vollständig ist.
class PositiveIntegers: def __init__(self, limit): self.counter = 0 self.limit = limit def __iter__(self): return self def __next__(self): self.counter += 1 if self.counter == self.limit:
Tipp 6: Wir zahlen für die Ausnahmebehandlung nur in Ausnahmesituationen. Vernachlässige sie nicht!
Es sollte einen - und vorzugsweise nur einen - den vorherigen Weg geben, dies zu tun.
switch
oder Pattern Matching? - Verwenden Sie if
und Wörterbücher. do-
? - dafür gibt es eine while
und for
. goto
? Ich denke du hast es selbst erraten. Gleiches gilt für einige Designtechniken und -muster, die in anderen Sprachen als selbstverständlich erscheinen. Das Erstaunlichste ist, dass es keine technischen Einschränkungen für ihre Implementierung gibt. Es ist nur "wir akzeptieren es nicht".
In Python wird beispielsweise das Muster "Builder" nicht häufig angezeigt. Stattdessen wird die Möglichkeit verwendet, Namensargumente an die Funktion zu übergeben und explizit anzufordern. Stattdessen
human = HumanBuilder.withName("Alex").withLastName("Black").ofAge(20).withHobbies(['tennis', 'programming']).build()
wird sein
human = Human( name="Alex" last_name="Black" age=20 hobbies=['tennis', 'programming'] )
Die Standardbibliothek verwendet keine Methodenketten für die Arbeit mit Sammlungen . Ich erinnere mich, wie mir ein Kollege aus der Welt von Kotlin den Code des folgenden Sinns zeigte (entnommen aus der offiziellen Dokumentation für Kotlin):
val shortGreetings = people .filter { it.name.length < 10 } .map { "Hello, ${it.name}!" }
In Python sind map()
, filter()
und viele andere Funktionen, keine Erfassungsmethoden. Wenn wir diesen Code einzeln umschreiben, erhalten wir:
short_greetings = map(lambda h: f"Hello, {h.name}", filter(lambda h: len(h.name) < 10, people))
Meiner Meinung nach sieht es schrecklich aus. Daher ist es für lange Bundles wie .takewhile().filter().map().reduce()
besser, das sogenannte zu verwenden Inklusion (Verständnis) oder gute alte Zyklen. Das gleiche Beispiel zu Kotlin wird übrigens in Form des entsprechenden Listenverständnisses gegeben. Und auf Python sieht es so aus:
short_greetings = [ f"Hello {h.name}" for h in people if len(h.name) < 10 ]
Für diejenigen, die die Ketten vermissenEs gibt Bibliotheken wie Pipe oder py_linq !
Methodenketten werden dort eingesetzt, wo sie effizienter sind als Standardwerkzeuge. Im Django-Webframework werden beispielsweise Ketten verwendet, um ein Datenbankabfrageobjekt zu erstellen:
query = User.objects \ .filter(last_visited__gte='2019-05-01') \ .order_by('username') \ .values('username', 'last_visited') \ [:5]
Tipp 7: Bevor Sie etwas tun, das aus früheren Erfahrungen sehr vertraut, in Python jedoch nicht bekannt ist, fragen Sie sich, welche Entscheidung ein erfahrener Pythonist treffen würde.
Python langsam
Ja
Ja, wenn es um die Ausführungsgeschwindigkeit im Vergleich zu statisch typisierten und kompilierten Sprachen geht.
Aber scheinen Sie eine detaillierte Antwort zu wollen?
Die Python-Referenzimplementierung (CPython) ist bei weitem nicht die effektivste Implementierung. Einer der wichtigen Gründe ist der Wunsch der Entwickler, dies nicht zu komplizieren. Und die Logik ist verständlich - nicht zu abstruser Code bedeutet weniger Fehler, eine bessere Möglichkeit, Änderungen vorzunehmen, und am Ende mehr Menschen, die diesen Code lesen, verstehen und ergänzen möchten.
Jake VanderPlas analysiert in seinem Blog, was in CPython unter der Haube passiert, wenn zwei Variablen mit ganzzahligen Werten hinzugefügt werden:
a = 1 b = 2 c = a + b
Selbst wenn wir nicht tief in den Dschungel von CPython vordringen, können wir sagen, dass der Interpreter zum Speichern der Variablen a
, b
und c
drei Objekte im Heap erstellen muss, in denen der Typ und (Zeiger auf) Werte gespeichert werden. Ermitteln Sie den Typ und die Werte während der Additionsoperation erneut, um so etwas wie binary_add<int, int>(a->val, b->val)
. Schreiben Sie das Ergebnis in c
.
Dies ist im Vergleich zu einem ähnlichen C-Programm schrecklich ineffizient.
Ein weiteres Problem mit CPython ist das sogenannte Global Interpreter Lock (GIL). Dieser Mechanismus, im Wesentlichen ein boolescher Wert, der von einem Mutex eingeschlossen wird, wird verwendet, um die Ausführung von Bytecode zu synchronisieren. GIL vereinfacht die Entwicklung von Code, der in einer Multithread-Umgebung ausgeführt wird: CPython muss nicht daran denken, den Zugriff auf Variablen oder Deadlocks zu synchronisieren. Sie müssen dafür bezahlen, dass nur ein Thread Zugriff erhält und den Bytecode zu einem bestimmten Zeitpunkt ausführt :

UPD: Dies bedeutet jedoch nicht, dass das Programm unter Python auf magische Weise in einer Multithread-Umgebung funktioniert! Der Code in Python wird nicht einzeln auf den Bytecode übertragen, und es gibt keine Garantie für die Kompatibilität des Bytecodes zwischen den Versionen! Daher müssen Sie die Threads im Code noch synchronisieren. Glücklicherweise verfügt Python hier über eine Vielzahl von Tools, mit denen Sie beispielsweise zwischen einem Multithread- und einem Multiprozess-Ausführungsmodell wechseln können.
Wenn Sie neugierig sind, welche Anstrengungen unternommen werden, um die GIL auszurotten ?
- . ( CFFI ) . API (extensions) C/C++. , Rust, Go Kotlin Native !
- , :
8: , . , IO (, , ) , , , , :)
? Linux MacOS, 95% . , 3., 2.7. Windows . : Docker, Windows Subsystem for Linux, Cygwin, , .
9: . , — - .
"Hello world" ? Großartig! machine learning- - Python Package Index (PyPI).
(packages), .. (virtual environments). , . - . pip
. pip
. , pipenv
poetry
— npm, bundler, cargo ..
0xA: — pip
virtualenv
. — , , . , — sys.path
— , .
Was kommt als nächstes?
? . :
Dive into python...
, . , , :)
, !