Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python Enhancement Proposals

PEP 328 – Imports: Mehrzeilig und Absolut/Relativ

Autor:
Aahz <aahz at pythoncraft.com>
Status:
Final
Typ:
Standards Track
Erstellt:
21. Dez. 2003
Python-Version:
2.4, 2.5, 2.6
Post-History:
08. Mär. 2004

Inhaltsverzeichnis

Zusammenfassung

Die import-Anweisung hat zwei Probleme

  • Lange import-Anweisungen können schwierig zu schreiben sein und erfordern verschiedene Verrenkungen, um den Python-Stilrichtlinien zu entsprechen.
  • Imports können angesichts von Paketen mehrdeutig sein; innerhalb eines Pakets ist nicht klar, ob import foo sich auf ein Modul innerhalb des Pakets oder auf ein Modul außerhalb des Pakets bezieht. (Genauer gesagt, ein lokales Modul oder Paket kann ein anderes überschatten, das direkt von sys.path hängt.)

Für das erste Problem wird vorgeschlagen, dass Klammern mehrere Namen umschließen dürfen, wodurch die Standardmechanismen von Python für mehrzeilige Werte angewendet werden können. Für das zweite Problem wird vorgeschlagen, dass alle import-Anweisungen standardmäßig absolut sein sollen (nur sys.path durchsuchen) mit spezieller Syntax (führende Punkte) für den Zugriff auf paketrelative Imports.

Zeitplan

In Python 2.5 müssen Sie das neue absolute Import-Verhalten aktivieren mit

from __future__ import absolute_import

Sie können relative Imports frei verwenden. In Python 2.6 wird jede import-Anweisung, die zu einem Intra-Package-Import führt, eine DeprecationWarning auslösen (dies gilt auch für from <> import, bei denen die relative Import-Syntax nicht verwendet wird).

Begründung für Klammern

Derzeit müssen Sie, wenn Sie viele Namen aus einem Modul oder Paket importieren möchten, eine von mehreren unappetitlichen Optionen wählen

  • Eine lange Zeile mit Backslash-Fortsetzungen schreiben
    from Tkinter import Tk, Frame, Button, Entry, Canvas, Text, \
        LEFT, DISABLED, NORMAL, RIDGE, END
    
  • Mehrere import-Anweisungen schreiben
    from Tkinter import Tk, Frame, Button, Entry, Canvas, Text
    from Tkinter import LEFT, DISABLED, NORMAL, RIDGE, END
    

(import * ist *keine* Option ;-)

Stattdessen sollte es möglich sein, den Standard-Gruppierungsmechanismus von Python (Klammern) zu verwenden, um die import-Anweisung zu schreiben

from Tkinter import (Tk, Frame, Button, Entry, Canvas, Text,
    LEFT, DISABLED, NORMAL, RIDGE, END)

Dieser Teil des Vorschlags hatte von Anfang an die Zustimmung des BDFL.

Die Klammerunterstützung wurde in Python 2.4 hinzugefügt.

Begründung für absolute Imports

In Python 2.4 und früheren Versionen ist es nicht klar, ob Sie beim Lesen eines Moduls, das sich innerhalb eines Pakets befindet,

import foo

sich auf ein Top-Level-Modul oder auf ein anderes Modul innerhalb des Pakets bezieht. Da die Bibliothek von Python wächst, überschatten immer mehr bestehende interne Paketmodule versehentlich Standardbibliotheksmodule. Dies ist insbesondere innerhalb von Paketen ein Problem, da es keine Möglichkeit gibt anzugeben, welches Modul gemeint ist. Um die Mehrdeutigkeit zu lösen, wird vorgeschlagen, dass foo immer ein Modul oder Paket ist, das von sys.path erreichbar ist. Dies nennt man einen absoluten Import.

Die python-dev-Community wählte absolute Imports als Standard, da sie der häufigere Anwendungsfall sind und da absolute Imports die gesamte Funktionalität relativer (Intra-Package-)Imports bieten können – wenn auch auf Kosten von Schwierigkeiten beim Umbenennen von Paketteilen weiter oben in der Hierarchie oder beim Verschieben eines Pakets in ein anderes.

Da dies eine semantische Änderung darstellt, sind absolute Imports in Python 2.5 und 2.6 optional durch die Verwendung von

from __future__ import absolute_import

Dieser Teil des Vorschlags hatte von Anfang an die Zustimmung des BDFL.

Begründung für relative Imports

Mit der Umstellung auf absolute Imports stellte sich die Frage, ob relative Imports überhaupt zulässig sein sollten. Es wurden verschiedene Anwendungsfälle präsentiert, von denen der wichtigste die Möglichkeit ist, die Struktur großer Pakete neu zu ordnen, ohne Unterpakete bearbeiten zu müssen. Außerdem kann ein Modul innerhalb eines Pakets sich selbst nicht ohne relative Imports leicht importieren.

Guido hat die Idee der relativen Imports genehmigt, aber es gab viel Uneinigkeit über die Schreibweise (Syntax). Es scheint Einigkeit darüber zu geben, dass relative Imports das Auflisten spezifischer zu importierender Namen erfordern werden (d. h. import foo als nackter Begriff wird immer ein absoluter Import sein).

Hier sind die Kandidaten

  • Einer von Guido
    from .foo import bar
    

    und

    from ...foo import bar
    

    Diese beiden Formen haben einige unterschiedliche vorgeschlagene Semantiken. Eine Semantik ist, dass jeder Punkt eine Ebene darstellt. Es gab viele Beschwerden über die Schwierigkeit, Punkte zu zählen. Eine andere Option ist, nur eine Ebene relativen Imports zuzulassen. Das verpasst viel Funktionalität, und die Leute beschwerten sich immer noch, dass ihnen der Punkt in der Ein-Punkt-Form fehlt. Die letzte Option ist, einen Algorithmus zum Auffinden relativer Module und Pakete zu definieren; das Einwände hier ist "Explizit ist besser als implizit". (Der vorgeschlagene Algorithmus ist "Suche von Verzeichnis des aktuellen Pakets nach oben, bis der ultimative Elternteil des Pakets erreicht ist".)

    Einige Leute haben andere Satzzeichen als Trennzeichen vorgeschlagen, wie z. B. "-" oder "^".

    Einige Leute haben die Verwendung von "*" vorgeschlagen

    from *.foo import bar
    
  • Die nächste Reihe von Optionen ist aus mehreren Posts zusammengestellt
    from __pkg__.__pkg__ import
    

    und

    from .__parent__.__parent__ import
    

    Viele Leute (Guido eingeschlossen) halten diese für hässlich, aber sie sind klar und explizit. Insgesamt bevorzugen mehr Leute __pkg__ als kürzere Option.

  • Ein Vorschlag war, nur Geschwisterreferenzen zuzulassen. Mit anderen Worten, Sie könnten keine relativen Imports verwenden, um auf Module höher im Paketbaum zu verweisen. Sie könnten dann entweder
    from .spam import eggs
    

    oder

    import .spam.eggs
    
  • Einige Leute bevorzugen die Zulassung von indizierten Eltern
    from -2.spam import eggs
    

    In diesem Szenario wäre der Import aus dem aktuellen Verzeichnis ein einfacher

    from .spam import eggs
    
  • Schließlich mögen einige Leute die Art und Weise nicht, wie sie import in from ... import ändern müssen, wenn sie in ein Paket hineinwechseln wollen. Sie schlagen vor, die import-Syntax komplett neu zu schreiben
    from MODULE import NAMES as RENAME searching HOW
    

    oder

    import NAMES as RENAME from MODULE searching HOW
        [from NAMES] [in WHERE] import ...
    

    Dies könnte jedoch höchstwahrscheinlich nicht für Python 2.5 implementiert werden (zu große Änderung), und die Zulassung relativer Imports ist ausreichend kritisch, dass wir etwas brauchen (da der Standard-import zu einem absoluten Import geändert wird). Mehr noch, diese vorgeschlagene Syntax hat mehrere offene Fragen

    • Was ist die genaue vorgeschlagene Syntax? (Welche Klauseln sind unter welchen Umständen optional?)
    • Wie stark bindet die searching-Klausel? Mit anderen Worten, schreiben Sie
      import foo as bar searching XXX, spam as ham searching XXX
      

      oder

      import foo as bar, spam as ham searching XXX
      

Guido's Entscheidung

Guido hat verkündet [1], dass relative Imports führende Punkte verwenden werden. Ein einzelner führender Punkt kennzeichnet einen relativen Import, der im aktuellen Paket beginnt. Zwei oder mehr führende Punkte ergeben einen relativen Import zu den Elternteilen des aktuellen Pakets, eine Ebene pro Punkt nach dem ersten. Hier ist ein Beispiel für ein Paketlayout

package/
    __init__.py
    subpackage1/
        __init__.py
        moduleX.py
        moduleY.py
    subpackage2/
        __init__.py
        moduleZ.py
    moduleA.py

Unter der Annahme, dass die aktuelle Datei entweder moduleX.py oder subpackage1/__init__.py ist, folgen hier korrekte Verwendungen der neuen Syntax

from .moduleY import spam
from .moduleY import spam as ham
from . import moduleY
from ..subpackage1 import moduleY
from ..subpackage2.moduleZ import eggs
from ..moduleA import foo
from ...package import bar
from ...sys import path

Beachten Sie, dass der letzte Fall zwar legal ist, aber sicherlich abgeraten wird ("irrsinnig" war das Wort, das Guido benutzte).

Relative Imports müssen immer from <> import verwenden; import <> ist immer absolut. Natürlich können absolute Imports from <> import verwenden, indem die führenden Punkte weggelassen werden. Der Grund, warum import .foo verboten ist, ist, dass nach

import XXX.YYY.ZZZ

dann

XXX.YYY.ZZZ

in einem Ausdruck verwendbar ist. Aber

.moduleY

ist nicht in einem Ausdruck verwendbar.

Relative Imports und __name__

Relative Imports verwenden das Attribut __name__ eines Moduls, um die Position dieses Moduls in der Paket-Hierarchie zu bestimmen. Wenn der Name des Moduls keine Paketinformationen enthält (z. B. es ist auf '__main__' gesetzt), dann werden relative Imports so aufgelöst, als ob das Modul ein Top-Level-Modul wäre, unabhängig davon, wo sich das Modul tatsächlich auf dem Dateisystem befindet.

Relative Imports und indirekte Einträge in sys.modules

Als Pakete eingeführt wurden, entstand das Konzept eines indirekten Eintrags in sys.modules [2]. Wenn ein Eintrag in sys.modules für ein Modul innerhalb eines Pakets den Wert None hatte, bedeutete dies, dass das Modul tatsächlich auf das Top-Level-Modul verwies. Zum Beispiel könnte 'Sound.Effects.string' den Wert None in sys.modules haben. Das bedeutete, dass jeder Import, der sich auf diesen Namen bezog, tatsächlich auf das Top-Level-Modul 'string' importierte.

Dies führte zu einer Optimierung für den Fall, dass ein relativer Import zu einem absoluten Import aufgelöst werden sollte. Aber da dieser PEP eine sehr klare Abgrenzung zwischen absoluten und relativen Imports macht, ist diese Optimierung nicht mehr notwendig. Wenn absolute/relative Imports die einzigen verfügbaren Import-Semantiken sind, werden indirekte Einträge in sys.modules nicht mehr unterstützt.

Referenzen

Weitere Hintergründe finden Sie in den folgenden python-dev-Threads


Quelle: https://github.com/python/peps/blob/main/peps/pep-0328.rst

Zuletzt geändert: 2025-02-01 08:59:27 GMT