11 Stimmen

Vorbereitungen für die Konvertierung von Python 2.x zu 3.x

Wie wir inzwischen alle wissen (so hoffe ich), beginnt Python 3 langsam Python 2.x zu ersetzen. Natürlich wird es noch viele VIELE Jahre dauern, bis der meiste bestehende Code endlich portiert ist, aber es gibt Dinge, die wir jetzt schon in unserem Code der Version 2.x tun können, um die Umstellung zu erleichtern.

Offensichtlich wird ein Blick auf Was gibt es Neues? in 3.x wird hilfreich sein, aber was können wir jetzt schon tun, um die bevorstehende Konvertierung schmerzloser zu machen (und um die Ausgabe von Updates für gleichzeitige Versionen zu erleichtern, falls erforderlich)? Ich denke insbesondere an Zeilen, mit denen wir unsere Skripte beginnen können, um frühere Versionen von Python 3.x ähnlicher zu machen, obwohl auch andere Gewohnheiten willkommen sind.

Der naheliegendste Code, der am Anfang des Skripts hinzugefügt werden sollte, ist folgender:

from __future__ import division
from __future__ import print_function
try:
    range = xrange
except NameError:
    pass

Die offensichtlichste Gewohnheit, die mir einfällt, ist "{0} {1}!".format("Hello", "World") für die String-Formatierung.

Gibt es noch andere Linien und gute Gewohnheiten, die man sich angewöhnen sollte?

12voto

bobince Punkte 512550

Das größte Problem, das durch Änderungen auf Mikroebene und 2to3 nicht angemessen gelöst werden kann, ist die Änderung des Standard-Stringtyps von Bytes zu Unicode.

Wenn Ihr Code irgendetwas mit Kodierungen und Byte-I/O zu tun hat, wird er einen Haufen manueller Arbeit benötigen, um korrekt zu konvertieren, so dass Dinge, die Bytes sein müssen, auch Bytes bleiben und in der richtigen Phase entsprechend dekodiert werden. Sie werden feststellen, dass einige String-Methoden (insbesondere format() ) und Bibliotheksaufrufe erfordern Unicode-Strings, so dass Sie möglicherweise zusätzliche Decodier-/Encodier-Zyklen benötigen, nur um die Strings als Unicode zu verwenden, auch wenn sie eigentlich nur Bytes sind.

Dies wird nicht durch die Tatsache, dass einige der Python-Standardbibliothek-Module wurden grob konvertiert mit 2to3 ohne ordnungsgemäße Aufmerksamkeit auf Bytes / Unicode / Kodierung Fragen, und so selbst Fehler machen, welche String-Typ geeignet ist, geholfen. Einige dieser Probleme werden derzeit behoben, aber zumindest von Python 3.0 bis 3.2 werden Sie mit verwirrendem und potentiell fehlerhaftem Verhalten von Paketen wie urllib, email und wsgiref konfrontiert, die über Byte-Kodierungen Bescheid wissen müssen.

Sie können das Problem abmildern, indem Sie jedes Mal, wenn Sie ein String-Literal schreiben, vorsichtig sind. Verwenden Sie u'' Zeichenfolgen für alles, was von Natur aus charakterbezogen ist, b'' Zeichenketten für alles, was wirklich Bytes sind, und '' für den 'Standard-String'-Typ, wenn es keine Rolle spielt oder Sie die String-Verwendungsanforderungen eines Bibliotheksaufrufs erfüllen müssen.

Leider ist die b'' Syntax wurde erst in Python 2.6 eingeführt, so dass Benutzer früherer Versionen von dieser Möglichkeit ausgeschlossen sind.

eta:

Was ist der Unterschied?

Oje. Nun...

Ein Byte enthält einen Wert im Bereich von 0-255 und kann eine Ladung von Binärdaten (z.B. den Inhalt eines Bildes) oder einen Text darstellen. In diesem Fall muss ein Standard gewählt werden, wie ein Satz von Zeichen in diese Bytes abgebildet wird. Die meisten dieser "Kodierungs"-Standards bilden den normalen "ASCII"-Zeichensatz auf die gleiche Weise auf die Bytes 0-127 ab, so dass es im Allgemeinen sicher ist, Byte-Strings für die Verarbeitung von reinem ASCII-Text in Python 2 zu verwenden.

Wenn Sie eines der Zeichen außerhalb des ASCII-Satzes in einer Byte-Zeichenkette verwenden wollen, haben Sie ein Problem, da jede Kodierung einen anderen Zeichensatz auf die verbleibenden Byte-Werte 128-255 abbildet, und die meisten Kodierungen können nicht jedes mögliche Zeichen auf Bytes abbilden. Dies ist die Quelle all jener Probleme, bei denen Sie eine Datei aus einem Gebietsschema in eine Windows-Anwendung in einem anderen Gebietsschema laden und sich alle akzentuierten oder nicht-lateinischen Buchstaben in die falschen verwandeln, was zu einem unlesbaren Chaos führt. (auch bekannt als "Mojibake".)

Es gibt auch "Multibyte"-Kodierungen, bei denen versucht wird, mehr Zeichen in den verfügbaren Platz einzupassen, indem für jedes Zeichen mehr als ein Byte verwendet wird. Diese Kodierungen wurden für ostasiatische Länder eingeführt, da es dort sehr viele chinesische Schriftzeichen gibt. Es gibt aber auch UTF-8, eine moderne Multibyte-Kodierung, die besser konzipiert ist und Folgendes aufnehmen kann jede Charakter.

Wenn Sie mit Byte-Strings in einer Multibyte-Kodierung arbeiten - und das werden Sie heute wahrscheinlich tun, weil UTF-8 sehr weit verbreitet ist; eigentlich sollte in einer modernen Anwendung keine andere Kodierung verwendet werden -, dann haben Sie noch mehr Probleme, als nur den Überblick darüber zu behalten, mit welcher Kodierung Sie spielen. len() wird Ihnen die Länge in Bytes mitteilen, nicht die Länge in Zeichen, und wenn Sie anfangen, die Bytes zu indizieren und zu verändern, ist es sehr wahrscheinlich, dass Sie eine Multibyte-Sequenz in zwei Teile zerlegen, eine ungültige Sequenz erzeugen und generell alles durcheinander bringen.

Aus diesem Grund haben Python 1.6 und spätere Versionen native Unicode-Strings (buchstabiert u'something' ), wobei jede Einheit in der Zeichenkette ein Zeichen und nicht ein Byte ist. Sie können len() Sie können sie zerschneiden, ersetzen, regexen, und sie werden sich immer angemessen verhalten. Für Textverarbeitungsaufgaben sind sie zweifellos besser, weshalb Python 3 sie zum Standard-String-Typ macht (ohne dass ein u vor der '' ).

Der Haken an der Sache ist, dass viele bestehende Schnittstellen, wie z. B. Dateinamen auf anderen Betriebssystemen als Windows, HTTP oder SMTP, in erster Linie bytebasiert sind und die Kodierung separat angegeben werden muss. Wenn Sie also mit Komponenten zu tun haben, die Bytes benötigen, müssen Sie darauf achten, Ihre Unicode-Zeichenfolgen korrekt in Bytes zu kodieren, und in Python 3 müssen Sie dies an einigen Stellen explizit tun, wo Sie es vorher nicht tun mussten.

Es ist ein internes Implementierungsdetail, dass Unicode-Strings intern "zwei Bytes" Speicherplatz pro Einheit benötigen. Sie bekommen diesen Speicherplatz nie zu sehen; Sie sollten ihn nicht in Bytes denken. Die Einheiten, an denen Sie arbeiten, sind konzeptionell Zeichen, unabhängig davon, wie Python sie im Speicher darstellen will.

...beiseite:

Das ist nicht ganz richtig. Auf "schmalen Builds" von Python wie dem Windows-Build ist jede Einheit einer Unicode-Zeichenkette technisch gesehen kein Zeichen, sondern eine UTF-16-"Codeeinheit". Für die Zeichen in der Basic Multilingual Plane, von 0x0000-0xFFFF, werden Sie keinen Unterschied bemerken, aber wenn Sie Zeichen außerhalb dieses 16-Bit-Bereichs verwenden, die in den "Astralebenen", werden Sie feststellen, dass sie zwei Einheiten statt einer benötigen, und auch hier besteht die Gefahr, dass Sie ein Zeichen aufspalten, wenn Sie es zerschneiden.

Das ist ziemlich schlecht und ist passiert, weil Windows (und andere, wie Java) sich auf UTF-16 als speicherinternen Speichermechanismus geeinigt haben, bevor Unicode über die Grenze von 65.000 Zeichen hinauswuchs. Allerdings ist die Verwendung dieser erweiterten Zeichen immer noch ziemlich selten, und jeder, der mit Windows arbeitet, wird daran gewöhnt sein, dass sie in vielen Anwendungen nicht mehr funktionieren, so dass es für Sie wahrscheinlich nicht kritisch ist.

Bei "Wide Builds" bestehen Unicode-Strings aus echten Zeichen-"Codepoint"-Einheiten, so dass auch die erweiterten Zeichen außerhalb des BMP konsistent und einfach gehandhabt werden können. Der Preis, der dafür zu zahlen ist, ist die Effizienz: Jede String-Einheit benötigt vier Bytes Speicherplatz.

5voto

eldarerathis Punkte 34379

Ich versuche, mir angewöhnt zu haben, Dinge zu benutzen wie var1//var2 immer dann, wenn ich tatsächlich eine Ganzzahldivision (und nicht eine Fließkommazahl) benötige. Kein großer Schritt in Richtung Python 3, aber zumindest muss ich nicht mehr alle meine Divisionen überprüfen :)

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X