PEP 414 – Expliziter Unicode-Literal für Python 3.3
- Autor:
- Armin Ronacher <armin.ronacher at active-4.com>, Alyssa Coghlan <ncoghlan at gmail.com>
- Status:
- Final
- Typ:
- Standards Track
- Erstellt:
- 15. Feb 2012
- Python-Version:
- 3.3
- Post-History:
- 28. Feb 2012, 04. Mrz 2012
- Resolution:
- Python-Dev Nachricht
Inhaltsverzeichnis
- Zusammenfassung
- BDFL-Verkündigung
- Vorschlag
- Ausschluss von „Raw“ Unicode-Literalen
- Hinweis des Autors
- Begründung
- Häufige Einwände
- Beschwerde: Diese PEP könnte die Übernahme von Python 3.2 beeinträchtigen
- Beschwerde: Python 3 sollte nicht verschlechtert werden, nur um die Portierung von Python 2 zu unterstützen
- Beschwerde: Das WSGI-Konzept „native Strings“ ist ein hässlicher Hack
- Beschwerde: Die vorhandenen Werkzeuge sollten für jeden gut genug sein
- Referenzen
- Urheberrecht
Zusammenfassung
Dieses Dokument schlägt die Wiedereinführung eines expliziten Unicode-Literals aus Python 2.x in die Python 3.x-Sprachspezifikation vor, um den Umfang der Änderungen zu reduzieren, die bei der Portierung Unicode-fähiger Python 2-Anwendungen nach Python 3 erforderlich sind.
BDFL-Verkündigung
Diese PEP wurde offiziell für Python 3.3 angenommen
Ich akzeptiere die PEP. Sie ist so harmlos, wie sie nur sein kann. Machen Sie es wahr.
Vorschlag
Diese PEP schlägt vor, dass Python 3.3 die Unterstützung für die Unicode-Literal-Syntax von Python 2 wiederherstellt und damit die Anzahl der Zeilen bestehenden Python 2-Codes in Unicode-fähigen Anwendungen, die ohne Änderungen unter Python 3 laufen werden, erheblich erhöht.
Insbesondere wird die Python 3-Definition für String-Literal-Präfixe erweitert, um zu erlauben
"u" | "U"
zusätzlich zu den derzeit unterstützten
"r" | "R"
Die folgenden sind alle gewöhnliche Python 3-Strings
'text'
"text"
'''text'''
"""text"""
u'text'
u"text"
u'''text'''
u"""text"""
U'text'
U"text"
U'''text'''
U"""text"""
Es werden keine Änderungen an der tatsächlichen Unicode-Handhabung von Python 3 vorgeschlagen, sondern nur an den akzeptablen Formen für String-Literale.
Ausschluss von „Raw“ Unicode-Literalen
Python 2 unterstützt das Konzept von „Raw“ Unicode-Literalen, die nicht der konventionellen Definition eines Raw-Strings entsprechen: Die Escape-Sequenzen \uXXXX und \UXXXXXXXX werden vom Compiler immer noch verarbeitet und beim Erstellen der zugehörigen Unicode-Objekte in die entsprechenden Unicode-Codepunkte umgewandelt.
Python 3 hat kein entsprechendes Konzept – der Compiler führt *keine* Vorverarbeitung des Inhalts von Raw-String-Literalen durch. Dies entspricht dem Verhalten von 8-Bit-Raw-String-Literalen in Python 2.
Da solche Strings selten verwendet werden und in Python 3 anders interpretiert würden, falls sie erlaubt wären, wurde beschlossen, sie vollständig wegzulassen. Code, der sie verwendet, wird somit immer noch sofort bei Python 3 fehlschlagen (mit einem Syntaxfehler), anstatt potenziell andere Ausgaben zu erzeugen.
Um ein äquivalentes Verhalten zu erzielen, das sowohl unter Python 2 als auch unter Python 3 läuft, kann entweder ein gewöhnliches Unicode-Literal verwendet werden (mit entsprechender zusätzlicher Maskierung innerhalb des Strings) oder String-Konkatenation oder String-Formatierung kann verwendet werden, um die Raw-Teile des Strings mit denen zu kombinieren, die die Verwendung von Unicode-Escape-Sequenzen erfordern.
Beachten Sie, dass bei der Verwendung von from __future__ import unicode_literals in Python 2 die nominell „Raw“ Unicode-String-Literale \uXXXX und \UXXXXXXXX Escape-Sequenzen verarbeiten, genau wie explizit mit dem „Raw Unicode“-Präfix markierte Python 2-Strings.
Begründung
Mit der Veröffentlichung einer Python 3-kompatiblen Version der Web Services Gateway Interface (WSGI)-Spezifikation (PEP 3333) für Python 3.2 bemühen sich viele Teile des Python-Web-Ökosystems, Python 3 zu unterstützen, ohne ihre bestehenden Entwickler- und Benutzergemeinschaften negativ zu beeinflussen.
Ein wichtiges Feedback von führenden Entwicklern aus diesen Communities, darunter Chris McDonough (WebOb, Pyramid), Armin Ronacher (Flask, Werkzeug), Jacob Kaplan-Moss (Django) und Kenneth Reitz (requests), ist, dass die Anforderung, die Schreibweise *jedes* Unicode-Literals in einer Anwendung (unabhängig davon, wie dies geschieht) zu ändern, ein entscheidender Stolperstein für Portierungsbemühungen ist.
Insbesondere können Framework- und Bibliotheksautoren im Gegensatz zu vielen anderen Python 3-Änderungen dies nicht einfach im Namen ihrer Benutzer handhaben. Die meisten dieser Benutzer kümmern sich wenig um die „Reinheit“ der Python-Sprachspezifikation, sie wollen nur, dass ihre Websites und Anwendungen so gut wie möglich funktionieren.
Obwohl die Python-Web-Community die lauteste Stimme bei der Hervorhebung dieser Bedenken ist, wird erwartet, dass andere stark Unicode-bewusste Domänen (wie die GUI-Entwicklung) ähnliche Probleme haben werden, wenn sie (und ihre Communities) sich intensiv bemühen, Python 3 zu unterstützen.
Häufige Einwände
Beschwerde: Diese PEP könnte die Übernahme von Python 3.2 beeinträchtigen
Diese Beschwerde ist interessant, da sie implizit zugibt, dass diese PEP die Portierung von Unicode-fähigen Python 2-Anwendungen nach Python 3 erleichtern *wird*.
Es gibt viele bestehende Python-Communities, die bereit sind, die Einschränkungen der bestehenden Portierungswerkzeuge in Kauf zu nehmen oder ihre Python 2-Codebasen ausreichend zu aktualisieren, um die Probleme zu minimieren.
Diese PEP ist nicht für diese Communities gedacht. Stattdessen wurde sie speziell entwickelt, um Leuten zu helfen, die diese Schwierigkeiten *nicht* in Kauf nehmen wollen.
Da der Vorschlag jedoch eine vergleichsweise kleine Änderung der Sprachsyntax ohne semantische Änderungen vorsieht, ist es möglich, ihn als externen Import-Hook zu unterstützen. Obwohl ein solcher Import-Hook einige Überlastungen bei der Importzeit verursacht und zusätzliche Schritte von jeder Anwendung erfordert, die ihn benötigt, um den Hook zu platzieren, ermöglicht er Anwendungen, die auf Python 3.2 abzielen, Bibliotheken und Frameworks zu verwenden, die sonst nur unter Python 3.3+ laufen würden, aufgrund ihrer Verwendung von Unicode-Literal-Präfixen.
Ein solches Import-Hook-Projekt ist Vinay Sajips uprefix [4].
Für diejenigen, die ihren Code lieber im Voraus übersetzen möchten, anstatt zur Importzeit umzuwandeln, arbeitet Armin Ronacher an einem Hook, der zur Installationszeit und nicht während des Imports läuft [5].
Die Kombination beider Ansätze ist natürlich auch möglich. Zum Beispiel könnte der Import-Hook für schnelle Edit-Test-Zyklen während der lokalen Entwicklung verwendet werden, der Installations-Hook jedoch für Continuous-Integration-Aufgaben und die Bereitstellung unter Python 3.2.
Die in diesem Abschnitt beschriebenen Ansätze können sich beispielsweise für Anwendungen als nützlich erweisen, die Python 3 auf der Ubuntu 12.04 LTS-Version ansprechen möchten, die Python 2.7 und 3.2 als offiziell unterstützte Python-Versionen mitliefern wird.
Beschwerde: Python 3 sollte nicht verschlechtert werden, nur um die Portierung von Python 2 zu unterstützen
Dies ist tatsächlich eines der Kern-Designprinzipien von Python 3. Eines der Kern-Designprinzipien von Python als Ganzes ist jedoch, dass „Praktikabilität über Reinheit siegt“. Wenn wir Drittentwicklern eine erhebliche Last aufbürden, sollten wir dafür eine solide Begründung haben.
In den meisten Fällen sind die Gründe für abwärtskompatible Python 3-Änderungen entweder zur Verbesserung der Code-Korrektheit (z. B. striktere Standardtrennung von Binär- und Textdaten und Integer-Division, die bei Bedarf in Floats umgewandelt wird), zur Reduzierung des typischen Speicherverbrauchs (z. B. verstärkte Nutzung von Iteratoren und Views anstelle von konkreten Listen) oder zur Entfernung störender Elemente, die Python-Code schwerer lesbar machen, ohne seine Ausdruckskraft zu erhöhen (z. B. die Komma-basierte Syntax für gefangene Ausnahmen). Änderungen, die durch solche Überlegungen gestützt werden, werden *nicht* zurückgenommen, unabhängig von Einwänden von Python 2-Entwicklern, die den Übergang zu Python 3 versuchen.
In vielen Fällen bot Python 2 aus historischen Gründen zwei Möglichkeiten, Dinge zu tun. Beispielsweise konnte Ungleichheit sowohl mit != als auch mit <> getestet werden, und Integer-Literale konnten mit einem optionalen L-Suffix angegeben werden. Solche Redundanzen wurden in Python 3 eliminiert, was die Gesamtgröße der Sprache reduziert und die Konsistenz für Entwickler verbessert.
Im ursprünglichen Design von Python 3 (bis einschließlich Python 3.2) wurde die explizite Präfix-Syntax für Unicode-Literale als in diese Kategorie fallend betrachtet, da sie in Python 3 völlig unnötig ist. Der Unterschied zu anderen Fällen und Unicode-Literalen besteht jedoch darin, dass der Unicode-Literal-Präfix in Python 2-Code *nicht* redundant ist: Er ist eine programmgesteuert signifikante Unterscheidung, die in irgendeiner Form erhalten bleiben muss, um Informationsverlust zu vermeiden.
Obwohl Portierungswerkzeuge zur Unterstützung des Übergangs erstellt wurden (siehe nächster Abschnitt), stellt dies immer noch eine zusätzliche Belastung für Benutzer von Unicode-Strings in Python 2 dar, nur damit zukünftigen Entwicklern, die Python 3 lernen, nicht gesagt werden muss: „Aus historischen Gründen können String-Literale ein optionales u- oder U-Präfix haben. Verwenden Sie dies niemals selbst, es ist nur dazu da, die Portierung von einer früheren Version der Sprache zu erleichtern.“
Viele Studenten, die Python 2 lernten, erhielten ähnliche Warnungen bezüglich String-Ausnahmen, ohne verwirrt oder irreparabel in ihrem Wachstum als Python-Entwickler gehemmt zu werden. Das Gleiche wird bei diesem Feature der Fall sein.
Dieser Punkt wird dadurch weiter untermauert, dass Python 3 *immer noch* die Großbuchstabenvarianten der B- und R-Präfixe für Bytes-Literale und Raw-Bytes- und String-Literale zulässt. Wenn die potenzielle Verwirrung durch String-Präfixvarianten so signifikant ist, wo war dann der Aufschrei, der verlangte, dass diese redundanten Präfixe zusammen mit all den anderen Redundanzen entfernt werden, die in Python 3 eliminiert wurden?
So wie die Unterstützung für String-Ausnahmen aus Python 2 über den normalen Deprecationsprozess entfernt wurde, kann die Unterstützung für redundante String-Präfixzeichen (insbesondere B, R, u, U) schließlich aus Python 3 entfernt werden, unabhängig von der aktuellen Annahme dieser PEP. Eine solche Änderung wird jedoch wahrscheinlich nur dann auftreten, wenn Drittanbieterbibliotheken, die Python 2.7 unterstützen, so verbreitet sind, wie Bibliotheken, die Python 2.2 oder 2.3 heute unterstützen.
Beschwerde: Das WSGI-Konzept „native Strings“ ist ein hässlicher Hack
Ein Grund, warum die Entfernung von Unicode-Literalen unter der Webentwicklungs-Community solche Bedenken hervorgerufen hat, ist, dass die aktualisierte WSGI-Spezifikation einige Kompromisse eingehen musste, um die Störung für bestehende Webserver, die eine WSGI-kompatible Schnittstelle bereitstellen, zu minimieren (dies wurde als notwendig erachtet, um den aktualisierten Standard zu einem tragfähigen Ziel für Webanwendungsautoren und Webframework-Entwickler zu machen).
Einer dieser Kompromisse ist das Konzept eines „nativen Strings“. WSGI definiert drei verschiedene Arten von Strings
- Text-Strings: behandelt als
unicodein Python 2 undstrin Python 3 - native Strings: behandelt als
strsowohl in Python 2 als auch in Python 3 - Binärdaten: behandelt als
strin Python 2 undbytesin Python 3
Einige Entwickler betrachten die „nativen Strings“ von WSGI als hässlichen Hack, da sie *explizit* als ausschließlich für latin-1 dekodierten „Text“ dokumentiert sind, unabhängig von der tatsächlichen Kodierung der zugrunde liegenden Daten. Die Verwendung dieses Ansatzes umgeht viele der Aktualisierungen des Datenmodells von Python 3, die darauf ausgelegt sind, die korrekte Handhabung von Textkodierungen zu fördern. Dies funktioniert jedoch im Allgemeinen aufgrund der spezifischen Details des Problembereichs – Webserver- und Webframework-Entwickler gehören zu den Personen, die am besten wissen, wie verschwommen die Linie zwischen Binärdaten und Text beim Arbeiten mit HTTP und verwandten Protokollen sein kann und wie wichtig es ist, die Auswirkungen der verwendeten Kodierungen beim Manipulieren von kodierten Textdaten zu verstehen. Auf der *Anwendungsebene* werden die meisten dieser Details vom Entwickler durch die Webframeworks und Support-Bibliotheken (sowohl in Python 2 *als auch* in Python 3) verborgen.
In der Praxis sind native Strings ein nützliches Konzept, da es einige APIs (sowohl in der Standardbibliothek als auch in Drittanbieter-Frameworks und -Paketen) und einige interne Interpreterdetails gibt, die hauptsächlich für die Arbeit mit str konzipiert sind. Diese Komponenten unterstützen oft *keine* unicode in Python 2 oder bytes in Python 3, oder, falls doch, erfordern sie zusätzliche Kodierungsdetails und/oder legen Beschränkungen auf, die nicht für die str-Varianten gelten.
Einige Beispiele für Schnittstellen, die am besten mit tatsächlichen str-Instanzen gehandhabt werden, sind
- Python-Bezeichner (als Attribute, Dictionary-Schlüssel, Klassennamen, Modulnamen, Import-Referenzen usw.)
- URLs meistens sowie HTTP-Header in urllib/http-Servern
- WSGI-Umgebungsschlüssel und CGI-vererbte Werte
- Python-Quellcode für dynamische Kompilierung und AST-Hacks
- Ausnahmemeldungen
__repr__Rückgabewert- bevorzugte Dateisystempfade
- bevorzugte Betriebssystemumgebung
In Python 2.6 und 2.7 werden diese Unterscheidungen am natürlichsten wie folgt ausgedrückt:
u"": Text-String (unicode)"": nativer String (str)b"": Binärdaten (str, auch alsbytesaliasisiert)
In Python 3 werden die latin-1 dekodierten nativen Strings nicht von anderen Text-Strings unterschieden
"": Text-String (str)"": nativer String (str)b"": Binärdaten (bytes)
Wenn from __future__ import unicode_literals verwendet wird, um das Verhalten von Python 2 zu ändern, dann kann, zusammen mit einer geeigneten Definition von n(), die Unterscheidung wie folgt ausgedrückt werden:
"": Text-Stringn(""): nativer Stringb"": Binärdaten
(Während n=str für einfache Fälle funktioniert, kann es manchmal Probleme geben, da Quellencodierungen nicht-ASCII sein können.)
Im gemeinsamen Teil von Python 2 und Python 3 (mit entsprechender Angabe einer Quellencodierung und Definitionen der Hilfsfunktionen u() und b()) können sie wie folgt ausgedrückt werden:
u(""): Text-String"": nativer Stringb(""): Binärdaten
Der letzte Ansatz ist die einzige Variante, die Python 2.5 und früher unterstützt.
Von allen Alternativen ist das in Python 2.6 und 2.7 derzeit unterstützte Format der bei weitem sauberste Ansatz, der die drei gewünschten Verhaltensweisen klar unterscheidet. Mit dieser PEP wird dieses Format auch in Python 3.3+ unterstützt. Es wird auch in Python 3.1 und 3.2 durch die Verwendung von Import- und Install-Hooks unterstützt. Obwohl es deutlich unwahrscheinlicher ist, ist es auch denkbar, dass die Hooks angepasst werden könnten, um die Verwendung des b-Präfixes auf Python 2.5 zu ermöglichen.
Beschwerde: Die vorhandenen Werkzeuge sollten für jeden gut genug sein
Ein häufig geäußerter Gedanke von Entwicklern, die Anwendungen bereits erfolgreich nach Python 3 portiert haben, lautet in etwa: „Wenn Sie es schwierig finden, tun Sie es falsch“ oder „Es ist nicht so schwer, probieren Sie es einfach!“. Obwohl dies zweifellos unbeabsichtigt ist, haben all diese Antworten den Effekt, den Leuten, die auf Mängel im aktuellen Portierungswerkzeugkasten hinweisen, zu sagen: „An den Portierungswerkzeugen ist nichts falsch, Sie sind einfach schlecht und wissen nicht, wie man sie richtig benutzt.“
Diese Antworten sind ein Fall des kompletten Verfehlens des Kernpunkts dessen, worüber sich die Leute beschweren. Das Feedback, das zu dieser PEP geführt hat, stammt nicht von Leuten, die sich beschweren, dass Ports nicht möglich sind. Stattdessen stammt das Feedback von Leuten, die Ports erfolgreich *abgeschlossen* haben und sich darüber beschweren, dass sie die Erfahrung für die Klasse von Anwendungen, die sie portieren mussten (insbesondere Unicode-fähige Web-Frameworks und Support-Bibliotheken), als sehr *unangenehm* empfanden.
Dies ist eine subjektive Einschätzung, und dies ist der Grund, warum das Python 3-Portierungswerkzeug-Ökosystem ein Fall ist, in dem die „eine offensichtliche Art, es zu tun“-Philosophie nachdrücklich *nicht* gilt. Obwohl ursprünglich beabsichtigt war, dass „in Python 2 entwickeln, mit 2to3 konvertieren, beides testen“ die Standardmethode sein würde, um für beide Versionen parallel zu entwickeln, haben sich in der Praxis die Bedürfnisse unterschiedlicher Projekte und Entwicklergemeinschaften als ausreichend vielfältig erwiesen, sodass eine Vielzahl von Ansätzen entwickelt wurde, die es jeder Gruppe ermöglichen, einen Ansatz zu wählen, der ihren Bedürfnissen am besten entspricht.
Lennart Regebro hat eine ausgezeichnete Übersicht über die verfügbaren Migrationsstrategien erstellt [2], und eine ähnliche Übersicht ist im offiziellen Portierungsleitfaden [3] zu finden. (Beachten Sie, dass sich die offizielle Anleitung seit Lennarts Übersicht zu „es kommt auf Ihre spezielle Situation an“ abgeschwächt hat.)
Beide Anleitungen gehen jedoch von der grundlegenden Annahme aus, dass alle beteiligten Entwickler sich *bereits* für die Idee der Unterstützung von Python 3 engagieren. Sie berücksichtigen nicht die *sozialen* Aspekte einer solchen Änderung, wenn man mit einer Benutzerbasis interagiert, die Störungen ohne klaren Nutzen möglicherweise nicht besonders toleriert, oder wenn man versucht, Python 2-fokussierte Upstream-Entwickler dazu zu bringen, Patches zu akzeptieren, die ausschließlich der Verbesserung der Python 3-Zukunftskompatibilität dienen.
Mit dem aktuellen Portierungswerkzeugsatz führen *jede* Migrationsstrategie zu Änderungen an *jedem* Unicode-Literal in einem Projekt. Keine Ausnahmen. Sie werden entweder in ein ungeprägtes String-Literal (wenn das Projekt die unicode_literals-Importanweisung übernimmt) oder in einen Konverteraufruf wie u("text") umgewandelt.
Wenn der unicode_literals-Importansatz verwendet wird, aber nicht gleichzeitig im gesamten Projekt übernommen wird, kann die Bedeutung eines leeren String-Literals ärgerlich mehrdeutig werden. Dieses Problem kann besonders heimtückisch für *aggregierte* Software sein, wie eine Django-Site – in einer solchen Situation können einige Dateien die unicode_literals-Importanweisung verwenden und andere nicht, was zu klaren Verwirrungspotenzialen führt.
Während diese Probleme technisch lösbar sind, sind sie auf sozialer Ebene eine völlig unnötige Ablenkung. Die Energie der Entwickler sollte für die Bewältigung *echter* technischer Schwierigkeiten im Zusammenhang mit dem Übergang zu Python 3 reserviert sein (wie die Unterscheidung ihrer 8-Bit-Text-Strings von ihren Binärdaten). Sie sollten nicht mit zusätzlichen Codeänderungen (auch automatisierten) bestraft werden, nur weil sie ihre Unicode-Strings in Python 2 *bereits* explizit identifiziert haben.
Armin Ronacher hat eine experimentelle Erweiterung für 2to3 erstellt, die Python-Code nur bis zu dem Grad modernisiert, dass er unter Python 2.7 oder neuer mit Unterstützung der Cross-Version-Kompatibilitätsbibliothek six läuft. Dieses Werkzeug ist als python-modernize verfügbar [1]. Derzeit betreffen die von diesem Werkzeug erzeugten Deltas jedes Unicode-Literal im konvertierten Quellcode. Dies wird zu berechtigten Bedenken bei Upstream-Entwicklern führen, die gebeten werden, solche Änderungen zu akzeptieren, und bei Framework- *Benutzern*, die gebeten werden, ihre Anwendungen zu ändern.
Durch die Eliminierung des Rauschens von Änderungen an der Unicode-Literal-Syntax könnten jedoch viele Projekte sauber und (vergleichsweise) unumstritten zukunftssicher für Python 3.3+ gemacht werden, indem einfach python-modernize ausgeführt und die empfohlenen Änderungen angewendet werden.
Referenzen
Urheberrecht
Dieses Dokument wurde gemeinfrei erklärt.
Quelle: https://github.com/python/peps/blob/main/peps/pep-0414.rst
Zuletzt geändert: 2025-02-01 08:59:27 GMT