Python-Optimierung: Wie sie Sie zu einem besseren Programmierer machen kann

Codementor Python-Experte und Stack Overflow-Legende Martijn Pieters nahm an einer Office Hour-Sitzung teil, um uns ein kurzes Tutorial zur Python-Optimierung zu geben.

Der folgende Text ist eine Zusammenfassung des Codementor-Teams und kann vom Originalvideo abweichen. Wenn Sie irgendwelche Probleme sehen, teilen Sie uns dies bitte mit!

Ein häufiger Fallstrick, den Python-Programmierer machen können

Zunächst einmal ist alles in Python ein Objekt, das im Gegensatz zu C oder einer anderen Programmiersprache Zahlen enthält. Ganzzahlen sind insbesondere auch Objekte, und jedes Mal, wenn ein Python-Programmierer eine Ganzzahl verwendet, muss Python das Objekt erstellen.

Um die Dinge jedoch zu beschleunigen und Programmierern das ständige Erstellen von Zahlen zu ersparen, sollte beachtet werden, dass häufig verwendete Ganzzahlen zwischen -5 und 256 in Python zwischengespeichert werden. Jedes Mal, wenn ein Python-Programmierer nach einer Zahl wie 5 fragt, hat Python das Objekt bereits und verwendet es jedes Mal, wenn die Zahl 5 aufgerufen wird. Mit anderen Worten, die ganzen Zahlen zwischen -5 und 256 sind Singletons, was bedeutet, dass es sich immer um dasselbe einzelne Objekt handelt und es nur eine Kopie jeder Zahl gibt. Dies ist möglich, weil Integer unveränderlich sind, da man ihren Wert nicht ändern kann.

Dieser Prozess der Verwendung von zwischengespeicherten Ganzzahlen schafft jedoch auch die Möglichkeit, dass Anfänger in Python-Programmierern über die beiden Vergleichsoperatoren in Python verwirrt werden: ist (Identität) und == (Gleichheit). Ist testet zwei Referenzen, um zu überprüfen, ob sie auf dasselbe Objekt zeigen, während == den Wert von zwei Objekten vergleicht, die dasselbe sein können oder nicht.

„Identität ist nicht gleich Gleichheit“

Einer der häufigsten Fehler, den Python-Anfänger machen, ist Verwirrung ist mit Gleichheit, und da häufig verwendete Ganzzahlen zwischengespeichert werden, bemerken sie ihren Fehler möglicherweise für eine lange Zeit nicht.

Nachfolgend zwei Beispiele zur Veranschaulichung des Problems:

//Beispiel 1//

In diesem Beispiel ergibt sich foo == bar als wahr, weil sie den gleichen Wert haben. Allerdings foo ist bar stellt sich auch als wahr heraus, weil Python für foo und bar genau das gleiche Objekt 42 gibt, anstatt ein weiteres zu erstellen. Wenn Sie also, wie das Beispiel zeigt, die Funktion id() verwenden, um die Speicheradresse der eindeutigen Bezeichner von foo und bar abzurufen, zeigen beide auf dieselbe Adresse. Anfänger von Python-Programmierern bemerken möglicherweise nicht, dass sie einen Fehler gemacht haben, und kommen zu erwarten ist gleichbedeutend mit Gleichheit sein.

Das Problem tritt jedoch auf, wenn größere Zahlen verwendet werden.
//Beispiel 2//

Da nur ganze Zahlen zwischen -5 und 256 gecacht werden, 10 * 1000 ist 10000 stellt sich als falsch heraus, da Python neue Objekte sowohl für 10 * 1000 als auch für 10.000 erstellt. Dies kann für einige Python-Anfänger ziemlich verwirrend sein, also müssen sie sich dessen bewusst sein ist bedeutet Identität und == bedeutet Gleichheit.

Interne Saiten für Effizienz

Der Python-Interpreter arbeitet viel mit Strings, und wie kleine Ganzzahlen können auch Strings von Python durch eine etwas andere Technik namens Interning wiederverwendet werden. Wenn eine neue Zeichenfolge erstellt wird, kann der Python-Interpreter wählen, ob eine zwischengespeicherte Kopie dieser Zeichenfolge gespeichert werden soll oder nicht. Dies geschieht unter Umständen, insbesondere bei Kennungen.

Wenn also ein String mit einem Buchstaben oder einem Unterstrich beginnt und nur Buchstaben, Unterstriche oder Zahlen enthält, interniert Python den String und erstellt einen Hash dafür. Da die meisten Dinge in Python Wörterbücher sind, muss Python viele Suchen nach Bezeichnern durchführen, und durch das Internieren von Bezeichnerzeichenfolgen kann der Suchvorgang erheblich beschleunigt werden. Mit anderen Worten, Bezeichner werden in einer Tabelle gespeichert, und Python erstellt einen Hash aus dem Zeichenfolgenobjekt für zukünftige Suchen. Eine solche Optimierung erfolgt während der Kompilierzeit, und Zeichenfolgenliterale, die wie Bezeichner aussehen, werden ebenfalls interniert.

Python-Programmierer können Interning nutzen, wenn sie ihre eigene Zeichenfolge verwenden, um Slots nachzuschlagen. Beispielsweise könnte es vorteilhaft sein, Interning zu verwenden, wenn mit einem großen Textverarbeitungsprogramm gearbeitet wird, das viele Ersetzungen oder Suchen erfordert und auf viele Netzwerknachrichten mit Abbildungen antwortet. Strings, die aus einer Datei oder Netzwerkverbindung gelesen werden, werden nicht interniert (obwohl Stringliterale interniert wurden), aber Sie können die Funktion intern() verwenden, um Ihre eigene internierte Kopie solcher Strings zu erstellen.

Guckloch-Optimierung

Damit Sie nicht jedes Mal ähnliche Objekte erstellen müssen, wenn der Code geladen wird, speichert Python sie als Literale.

//Beispiel 3//

In diesem Code werden zwei Literale verwendet – ein String-Literal und ein Integer-Literal. Es gibt jetzt ein Code-Objekt, das „foo“ zugewiesen ist, und eine damit verbundene Konstante. So werden im obigen Beispiel none, hello world, space (‘ ‘) und die Zahl 20 alle als Konstanten gespeichert.

Abgesehen vom Speichern von Konstanten verfügt Python über mehrere weitere Optimierungstricks, z. B. das Vereinfachen verschiedener Arten von Ausdrücken und das Ersetzen bestimmter veränderlicher Objekte durch unveränderliche Objekte.

Die Ausdrücke, die Python vereinfacht, sind Berechnungen im Code, sodass Programmierer Funktionen als Berechnungen speichern können, da der Peephole-Optimierer von Python das Ergebnis des Ausdrucks speichert, anstatt es jedes Mal neu zu berechnen, wenn die Funktion verwendet wird. Grundsätzlich wird alles, was unveränderlich ist und einen Ausdruck hat, vorberechnet und gespeichert, was jede Art von Sequenz enthält, z. B. Zeichenfolgen. Wenn ein String aus mehreren Teilen besteht und mit einer Klasse zusammengesetzt wird, wird er durch das Endergebnis ersetzt. Alles, was länger als 20 Zeichen ist, wird jedoch ignoriert.

//Beispiel 4//

Im obigen Beispiel wurden „duration“ und „neener“ beide in das Ergebnis erweitert, sodass Python jedes Mal 864.000 lädt, wenn auf „duration“ verwiesen wird, und Python lädt neenerneenereener für den Ausdruck „neener“. Das Tupel von big_constant wurde jedoch nicht erweitert, da der Peephole-Optimierer intelligent mit dem Speicherplatz umgeht. Ein Tupel von 3000 Elementen macht eine Bytecode-Datei ungewöhnlich groß, weshalb eine Sequenz höchstens mit dem 20-fachen multipliziert werden kann. Der Peephole-Optimierung sind also Grenzen gesetzt, da Strings oft länger als 20 Zeichen sind. Nichtsdestotrotz ist die Peephole-Optimierung immer noch recht nützlich, um Programmierern zu ersparen, sich um all die Konstanten kümmern zu müssen, die sie möglicherweise verwenden.

Darüber hinaus kann die Peephole-Optimierung, wie oben erwähnt, auch Mutables durch Immutables ersetzen. Ein veränderliches Mengenliteral kann im Prinzip geändert werden, da es sich tatsächlich um ein unveränderliches Objekt handelt, wie ein Listenobjekt.

Im Beispiel auf dieser Folie ist an in test wird verwendet, um die Zugehörigkeit zu einem Literal zu überprüfen, und Python ersetzt die Menge durch eine eingefrorene Menge, die im Code gespeichert wird. Daher kann und sollte man Mengen immer dann verwenden, wenn es notwendig ist, auf Zugehörigkeit zu testen, da der Test eine Operation mit konstanten Kosten hat. Mit anderen Worten, egal wie groß die Menge ist, Python benötigt immer die gleiche Zeit, um die Mitgliedschaft zu validieren, und dies ist immer schneller als das Testen gegen ein Tupel oder eine Liste.

Darüber hinaus macht Python dasselbe mit Listen. Wenn ein wörtliches Listenobjekt in einen Mitgliedschaftstest eingefügt wird, ersetzt Python es durch eine tatsächliche Tupelkonstante im Code, ähnlich wie im folgenden Beispiel.

Um zu sehen, was bei der Codeoptimierung passiert und was Python tut, kann man das Disassemblierungstool verwenden. Das Modul gibt den ursprünglichen Bytecode oder Codeabschnitt zurück, der in einer Funktion übergeben werden kann, um zu sehen, wie das Codeobjekt aussieht.

Es ist wichtig zu beachten, dass nur Literale funktionieren. Wenn die Menge oder Liste kein Literal ist, findet die Optimierung nicht statt. Dies gilt jedoch nicht für Listen wie Tupel, da ein Tupel mit einer wörtlichen Syntax mit den Klammern erstellt und daher als einzelnes, konstantes Objekt gespeichert werden kann.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *