PEP 236 – Zurück in die __future__
- Autor:
- Tim Peters <tim.peters at gmail.com>
- Status:
- Final
- Typ:
- Standards Track
- Erstellt:
- 26. Feb. 2001
- Python-Version:
- 2.1
- Post-History:
- 26. Feb. 2001
Inhaltsverzeichnis
- Motivation
- Absicht
- Syntax
- Semantik
- Beispiel
- Standardmodul __future__.py
- Gelöstes Problem: Laufzeitkompilierung
- Gelöstes Problem: Native interaktive Shells
- Gelöstes Problem: Simulierte interaktive Shells
- Fragen und Antworten
- Wie wäre es mit einer „from __past__“-Version, um zum alten Verhalten zurückzukehren?
- Wie sieht es mit Inkompatibilitäten aufgrund von Änderungen an der Python-Virtuellen Maschine aus?
- Wie sieht es mit Inkompatibilitäten aufgrund von Änderungen an der C-API von Python aus?
- Ich möchte future_statements in try/except-Blöcke einschließen, damit ich je nach Python-Version, die ich verwende, unterschiedlichen Code verwenden kann. Warum kann ich das nicht?
- Wenn wir zum Beispiel nested_scopes zurückgehen, was passiert, wenn Version 2.2 herauskommt und ich meinen Code immer noch nicht geändert habe? Wie kann ich dann das 2.1-Verhalten beibehalten?
- Das Überladen von
importist mistig. Warum wird dafür keine neue Anweisung eingeführt?
- Urheberrecht
- Referenzen und Fußnoten
Motivation
Von Zeit zu Zeit nimmt Python eine inkompatible Änderung an der beworbenen Semantik von Kernkonstrukten vor oder ändert deren unbeabsichtigtes (implementierungsabhängiges) Verhalten auf irgendeine Weise. Obwohl dies nie willkürlich geschieht und immer mit dem Ziel geschieht, die Sprache langfristig zu verbessern, ist es kurzfristig umstritten und störend.
PEP 5, Leitlinien für die Sprachenentwicklung, schlägt Wege vor, um den Schmerz zu lindern, und dieser PEP führt einige Mechanismen zur Unterstützung dessen ein.
PEP 227, Statisch verschachtelte Geltungsbereiche, ist die erste Anwendung und wird hier als Beispiel verwendet.
Absicht
[Hinweis: Dies ist eine Richtlinie und sollte daher schließlich in PEP 5 überführt werden]
Wenn eine inkompatible Änderung an der Kernsprachsyntax oder -semantik vorgenommen wird
- Die Version C, die die Änderung einführt, ändert die Syntax oder Semantik nicht standardmäßig.
- Eine zukünftige Version R wird identifiziert, in der die neue Syntax oder Semantik erzwungen wird.
- Die in PEP 230, Warnungsframework, beschriebenen Mechanismen werden verwendet, um, wann immer möglich, Warnungen über Konstrukte oder Operationen zu generieren, deren Bedeutung sich in Version R ändern [1] könnte.
- Die neue future_statement (siehe unten) kann explizit in ein Modul M aufgenommen werden, um anzufordern, dass der Code in Modul M die neue Syntax oder Semantik in der aktuellen Version C verwendet.
Alter Code funktioniert also standardmäßig mindestens eine Version lang weiterhin, obwohl er möglicherweise neue Warnmeldungen generiert. Die Migration zur neuen Syntax oder Semantik kann während dieser Zeit erfolgen, wobei future_statement verwendet wird, um Module, die sie enthalten, so wirken zu lassen, als ob die neue Syntax oder Semantik bereits erzwungen würde.
Beachten Sie, dass die future_statement-Mechanismen für neue Funktionen nicht erforderlich sind, es sei denn, sie können bestehenden Code brechen; vollständig abwärtskompatible Ergänzungen können – und sollten – ohne eine entsprechende future_statement eingeführt werden.
Syntax
Eine future_statement ist einfach eine from/import-Anweisung, die den reservierten Modulnamen __future__ verwendet.
future_statement: "from" "__future__" "import" feature ["as" name]
(","feature ["as" name])*
feature: identifier
name: identifier
Außerdem müssen alle future_statements am Anfang des Moduls stehen. Die einzigen Zeilen, die vor einer future_statement stehen können, sind
- Der Modul-Docstring (falls vorhanden).
- Kommentare.
- Leere Zeilen.
- Andere future_statements.
Beispiel
"""This is a module docstring."""
# This is a comment, preceded by a blank line and followed by
# a future_statement.
from __future__ import nested_scopes
from math import sin
from __future__ import alabaster_weenoblobs # compile-time error!
# That was an error because preceded by a non-future_statement.
Semantik
Eine future_statement wird zur Kompilierzeit erkannt und speziell behandelt: Änderungen an der Semantik von Kernkonstrukten werden oft durch die Generierung von anderem Code implementiert. Es kann sogar sein, dass eine neue Funktion neue inkompatible Syntax einführt (wie ein neues reserviertes Wort), in diesem Fall muss der Compiler das Modul möglicherweise anders parsen. Solche Entscheidungen können nicht bis zur Laufzeit verschoben werden.
Für jede Version kennt der Compiler die definierten Feature-Namen und löst einen Kompilierungsfehler aus, wenn eine future_statement ein ihm unbekanntes Feature enthält [2].
Die direkten Laufzeitsemantiken sind dieselben wie für jede import-Anweisung: Es gibt ein Standardmodul __future__.py, das später beschrieben wird, und es wird zur Zeit der Ausführung der future_statement wie üblich importiert.
Die *interessanten* Laufzeitsemantiken hängen von den spezifischen Funktionen ab, die von den future_statement(s) im Modul "importiert" werden.
Beachten Sie, dass die Anweisung
import __future__ [as name]
ist keine future_statement; es ist eine normale Importanweisung ohne besondere Semantik oder Syntaxbeschränkungen.
Beispiel
Betrachten Sie diesen Code in Datei scope.py
x = 42
def f():
x = 666
def g():
print "x is", x
g()
f()
Unter 2.0 gibt es aus
x is 42
Verschachtelte Geltungsbereiche (PEP 227) werden in 2.1 eingeführt. Aber unter 2.1 gibt es immer noch aus
x is 42
und generiert auch eine Warnung.
In 2.2 und auch in 2.1, *wenn* from __future__ import nested_scopes am Anfang von scope.py enthalten ist, gibt es aus
x is 666
Standardmodul __future__.py
Lib/__future__.py ist ein echtes Modul und dient drei Zwecken
- Um bestehende Tools, die Importanweisungen analysieren und erwarten, die importierten Module zu finden, nicht zu verwirren.
- Um sicherzustellen, dass future_statements unter Versionen vor 2.1 mindestens Laufzeitfehler auslösen (der Import von
__future__schlägt fehl, da es vor 2.1 kein Modul dieses Namens gab). - Um zu dokumentieren, wann inkompatible Änderungen eingeführt wurden und wann sie obligatorisch sein werden – oder waren. Dies ist eine Form ausführbarer Dokumentation und kann programmgesteuert durch Importieren von
__future__und Untersuchen seines Inhalts abgefragt werden.
Jede Anweisung in __future__.py hat die Form
FeatureName = "_Feature(" OptionalRelease "," MandatoryRelease ")"
wobei normalerweise *OptionalRelease* < *MandatoryRelease*, und beide sind 5-Tupel der gleichen Form wie sys.version_info
(PY_MAJOR_VERSION, # the 2 in 2.1.0a3; an int
PY_MINOR_VERSION, # the 1; an int
PY_MICRO_VERSION, # the 0; an int
PY_RELEASE_LEVEL, # "alpha", "beta", "candidate" or "final"; string
PY_RELEASE_SERIAL # the 3; an int )
*OptionalRelease* zeichnet die erste Version auf, in der
from __future__ import FeatureName
akzeptiert wurde.
Im Falle von *MandatoryReleases*, die noch nicht stattgefunden haben, prognostiziert *MandatoryRelease* die Version, in der die Funktion Teil der Sprache wird.
Andernfalls zeichnet *MandatoryRelease* auf, wann die Funktion Teil der Sprache wurde; in Versionen ab diesem Zeitpunkt müssen Module nicht mehr
from __future__ import FeatureName
die fragliche Funktion verwenden, können solche Importe aber weiterhin verwenden.
*MandatoryRelease* kann auch None sein, was bedeutet, dass eine geplante Funktion verworfen wurde.
Instanzen von class _Feature haben zwei entsprechende Methoden, .getOptionalRelease() und .getMandatoryRelease().
Keine Feature-Zeile wird jemals aus __future__.py gelöscht.
Beispielzeile
nested_scopes = _Feature((2, 1, 0, "beta", 1), (2, 2, 0, "final", 0))
Das bedeutet, dass
from __future__ import nested_scopes
in allen Versionen ab 2.1b1 funktioniert und dass verschachtelte Geltungsbereiche ab Version 2.2 erzwungen werden sollen.
Gelöstes Problem: Laufzeitkompilierung
Mehrere Python-Funktionen können Code zur Laufzeit eines Moduls kompilieren
- Die
exec-Anweisung. - Die Funktion
execfile(). - Die Funktion
compile(). - Die Funktion
eval(). - Die Funktion
input().
Da ein Modul M, das eine future_statement mit dem Feature F enthält, explizit fordert, dass die aktuelle Version sich in Bezug auf F wie eine zukünftige Version verhält, sollte jeder dynamisch aus Text kompilierte Code, der an eine dieser Funktionen innerhalb von M übergeben wird, wahrscheinlich auch die neue Syntax oder Semantik von F verwenden. Die Version 2.1 verhält sich so.
Dies ist jedoch nicht immer erwünscht. Zum Beispiel kompiliert doctest.testmod(M) Beispiele aus Strings in M, und diese Beispiele sollten die Entscheidungen von M verwenden, nicht unbedingt die des doctest-Moduls. In der Version 2.1 ist dies nicht möglich, und es wurde bisher kein Schema vorgeschlagen, um dies zu umgehen. HINWEIS: PEP 264 befasste sich später flexibel damit, indem optionale Argumente zu compile() hinzugefügt wurden.
In jedem Fall gilt eine future_statement, die "nahe am Anfang" (siehe Syntax oben) von dynamisch kompiliertem Text durch eine exec, execfile() oder compile() erscheint, für den generierten Codeblock, hat aber keine weitere Auswirkung auf das Modul, das eine solche exec, execfile() oder compile() ausführt. Dies kann jedoch nicht verwendet werden, um eval() oder input() zu beeinflussen, da diese nur Ausdruckseingaben zulassen und eine future_statement kein Ausdruck ist.
Gelöstes Problem: Native interaktive Shells
Es gibt zwei Möglichkeiten, eine interaktive Shell zu erhalten
- Durch Aufruf von Python von einer Kommandozeile ohne Skriptargument.
- Durch Aufruf von Python von einer Kommandozeile mit dem Schalter
-iund mit einem Skriptargument.
Eine interaktive Shell kann als Extremfall der Laufzeitkompilierung betrachtet werden (siehe oben): Im Wesentlichen führt jede Anweisung, die an einer interaktiven Shell-Eingabeaufforderung eingegeben wird, eine neue Instanz von exec, compile() oder execfile() aus. Eine in einer interaktiven Shell eingegebene future_statement gilt für den Rest der Shell-Sitzung, als ob die future_statement am Anfang eines Moduls gestanden hätte.
Gelöstes Problem: Simulierte interaktive Shells
Von Hand erstellte interaktive Shells (von Tools wie IDLE und Emacs Python-Mode) sollten sich wie native interaktive Shells verhalten (siehe oben). Die intern von nativen interaktiven Shells verwendeten Mechanismen wurden jedoch nicht freigelegt, und es gibt keinen klaren Weg für Tools, die ihre eigenen interaktiven Shells erstellen, das gewünschte Verhalten zu erzielen.
HINWEIS: PEP 264 befasste sich später damit, indem die Standardbibliothek codeop.py mit Intelligenz erweitert wurde. Simulierte Shells, die nicht die Helfer der Standardbibliothek verwenden, können einen ähnlichen Effekt erzielen, indem sie die neuen optionalen Argumente zu compile() nutzen, die von PEP 264 hinzugefügt wurden.
Fragen und Antworten
Wie wäre es mit einer „from __past__“-Version, um zum alten Verhalten zurückzukehren?
Außerhalb des Rahmens dieses PEP. Scheint dem Autor unwahrscheinlich, obwohl. Schreiben Sie einen PEP, wenn Sie ihn verfolgen möchten.
Wie sieht es mit Inkompatibilitäten aufgrund von Änderungen an der Python-Virtuellen Maschine aus?
Außerhalb des Rahmens dieses PEP, obwohl PEP 5 dort auch eine Gnadenfrist vorschlägt, und die future_statement könnte dort ebenfalls eine Rolle spielen.
Wie sieht es mit Inkompatibilitäten aufgrund von Änderungen an der C-API von Python aus?
Außerhalb des Rahmens dieses PEP.
Ich möchte future_statements in try/except-Blöcke einschließen, damit ich je nach Python-Version, die ich verwende, unterschiedlichen Code verwenden kann. Warum kann ich das nicht?
Entschuldigung! try/except ist ein Laufzeit-Feature; future_statements sind hauptsächlich Kompilierungszeit-Gimmicks, und Ihr try/except geschieht lange, nachdem der Compiler fertig ist. Das heißt, bis Sie try/except ausführen, sind die für das Modul geltenden Semantiken bereits eine getroffene Sache. Da try/except nicht das tut, was es zu tun scheint, ist es einfach nicht erlaubt. Außerdem möchten wir diese speziellen Anweisungen sehr leicht auffindbar und erkennbar halten.
Beachten Sie, dass Sie __future__ direkt importieren und die darin enthaltenen Informationen zusammen mit sys.version_info verwenden können, um festzustellen, wie sich die von Ihnen verwendete Version zur gegebenen Funktion verhält.
Wenn wir zum Beispiel nested_scopes zurückgehen, was passiert, wenn Version 2.2 herauskommt und ich meinen Code immer noch nicht geändert habe? Wie kann ich dann das 2.1-Verhalten beibehalten?
Indem Sie weiterhin 2.1 verwenden und nicht zu 2.2 wechseln, bis Sie Ihren Code ändern. Der Zweck von future_statement ist es, das Leben für Leute zu erleichtern, die mit der neuesten Version rechtzeitig auf dem Laufenden bleiben. Wir hassen Sie nicht, wenn Sie es nicht tun, aber Ihre Probleme sind viel schwieriger zu lösen, und jemand mit diesen Problemen muss einen PEP schreiben, der sich damit befasst. future_statement richtet sich an ein anderes Publikum.
Das Überladen von import ist mistig. Warum wird dafür keine neue Anweisung eingeführt?
Wie wäre es mit lambda lambda nested_scopes? Das heißt, es sei denn, wir führen ein neues Schlüsselwort ein, wir können keine vollständig neue Anweisung einführen. Aber wenn wir ein neues Schlüsselwort einführen, würde das alte Code brechen. Das wäre zu ironisch, um es zu ertragen. Ja, das Überladen von import ist Mist, aber nicht so energisch wie die Alternativen – wie sie sind, sind future_statements 100% abwärtskompatibel.
Urheberrecht
Dieses Dokument wurde gemeinfrei erklärt.
Referenzen und Fußnoten
Quelle: https://github.com/python/peps/blob/main/peps/pep-0236.rst
Zuletzt geändert: 2025-02-01 08:59:27 GMT