PEP 328 – 匯入:多行和絕對/相對
- 作者:
- Aahz <aahz at pythoncraft.com>
- 狀態:
- 最終版
- 型別:
- 標準跟蹤
- 建立日期:
- 2003年12月21日
- Python 版本:
- 2.4, 2.5, 2.6
- 釋出歷史:
- 2004年3月8日
摘要
import 語句有兩個問題
- 長的
import語句可能難以編寫,需要各種扭曲才能符合Python風格指南。 - 在包面前,匯入可能不明確;在包內部,不清楚
import foo是指包內的模組還是包外的某個模組。(更準確地說,本地模組或包可以遮蓋直接掛在sys.path上的另一個模組。)
對於第一個問題,建議允許括號括住多個名稱,從而允許Python用於多行值的標準機制應用。對於第二個問題,建議所有 import 語句預設都是絕對的(僅搜尋 sys.path),並使用特殊語法(前導點)來訪問包相對匯入。
時間線
在Python 2.5中,您必須使用以下語句啟用新的絕對匯入行為
from __future__ import absolute_import
您可以自由使用相對匯入。在Python 2.6中,任何導致包內匯入的 import 語句都將引發 DeprecationWarning(這也適用於未能使用相對匯入語法的 from <> import)。
括號的理由
目前,如果您想從模組或包中匯入許多名稱,您必須選擇以下幾個不受歡迎的選項之一
- 用反斜槓續行寫一個長行
from Tkinter import Tk, Frame, Button, Entry, Canvas, Text, \ LEFT, DISABLED, NORMAL, RIDGE, END
- 編寫多個
import語句from Tkinter import Tk, Frame, Button, Entry, Canvas, Text from Tkinter import LEFT, DISABLED, NORMAL, RIDGE, END
(import * 不是一個選項 ;-))
相反,應該可以使用Python的標準分組機制(括號)來編寫 import 語句
from Tkinter import (Tk, Frame, Button, Entry, Canvas, Text,
LEFT, DISABLED, NORMAL, RIDGE, END)
該提案的這一部分從一開始就獲得了BDFL的批准。
Python 2.4 中添加了括號支援。
絕對匯入的理由
在Python 2.4及更早版本中,如果您正在讀取包內的模組,不清楚
import foo
是指頂級模組還是包內的另一個模組。隨著Python庫的擴充套件,越來越多的現有包內部模組意外地遮蔽了標準庫模組。這在包內部尤其是一個困難的問題,因為無法指定要使用哪個模組。為了解決歧義,建議 foo 始終是可從 sys.path 訪問的模組或包。這稱為絕對匯入。
python-dev 社群選擇絕對匯入作為預設值,因為它們是更常見的用例,並且因為絕對匯入可以提供相對(包內)匯入的所有功能——儘管代價是在重新命名層次結構中較高位置的包部分或將一個包移動到另一個包內時會遇到困難。
由於這代表了語義上的變化,絕對匯入在Python 2.5和2.6中將是可選的,透過使用
from __future__ import absolute_import
該提案的這一部分從一開始就獲得了BDFL的批准。
相對匯入的理由
隨著向絕對匯入的轉變,出現了一個問題,即是否應該允許相對匯入。提出了幾個用例,其中最重要的是能夠在不編輯子包的情況下重新安排大型包的結構。此外,包內的模組在沒有相對匯入的情況下無法輕鬆匯入自身。
Guido 批准了相對匯入的想法,但在拼寫(語法)方面存在很多分歧。似乎大家一致認為相對匯入將需要列出要匯入的特定名稱(也就是說,import foo 作為裸項將始終是絕對匯入)。
以下是競爭者
- Guido的其中一個
from .foo import bar
和
from ...foo import bar
這兩種形式有幾種不同的建議語義。一種語義是讓每個點代表一個級別。關於計算點的困難有很多抱怨。另一個選項是隻允許一個級別的相對匯入。這缺少很多功能,人們仍然抱怨一個點形式中缺少點。最後一個選項是定義一個用於查詢相對模組和包的演算法;這裡的反對意見是“顯式優於隱式”。(建議的演算法是“從當前包目錄向上搜尋,直到達到最終包父級”。)
有人建議使用其他標點符號作為分隔符,例如“-”或“^”。
有人建議使用“*”
from *.foo import bar
- 下一組選項是綜合了多位發帖人的意見
from __pkg__.__pkg__ import
和
from .__parent__.__parent__ import
許多人(包括Guido)認為這些看起來很難看,但它們*確實*清晰明確。總的來說,更多人喜歡
__pkg__作為更短的選項。 - 一個建議是隻允許同級引用。換句話說,您不能使用相對匯入來引用包樹中更高層的模組。然後您可以選擇
from .spam import eggs
或
import .spam.eggs
- 有些人傾向於允許索引父級
from -2.spam import eggs
在這種情況下,從當前目錄匯入將很簡單
from .spam import eggs
- 最後,有些人不喜歡當您想深入包內部時必須將
import更改為from ... import的方式。他們建議完全重寫import語法from MODULE import NAMES as RENAME searching HOW
或
import NAMES as RENAME from MODULE searching HOW [from NAMES] [in WHERE] import ...
然而,這很可能無法在 Python 2.5 中實現(改動太大),而且允許相對匯入至關重要,因此我們現在需要一些東西(鑑於標準
import將改為絕對匯入)。更重要的是,這個提議的語法還有幾個懸而未決的問題- 提議的精確語法是什麼?(在什麼情況下哪些子句是可選的?)
searching子句的繫結強度如何?換句話說,您是這樣寫的嗎import foo as bar searching XXX, spam as ham searching XXX
或
import foo as bar, spam as ham searching XXX
Guido的決定
Guido已宣佈 [1] 相對匯入將使用前導點。單個前導點表示相對匯入,從當前包開始。兩個或更多前導點表示相對匯入到當前包的父級(或多個父級),第一個點之後每點一個級別。這是一個示例包佈局
package/
__init__.py
subpackage1/
__init__.py
moduleX.py
moduleY.py
subpackage2/
__init__.py
moduleZ.py
moduleA.py
假設當前檔案是 moduleX.py 或 subpackage1/__init__.py,以下是新語法的正確用法
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
請注意,雖然最後一種情況是合法的,但它無疑是不鼓勵的(Guido 用的是“瘋狂”這個詞)。
相對匯入必須始終使用 from <> import;import <> 始終是絕對的。當然,絕對匯入可以透過省略前導點來使用 from <> import。禁止 import .foo 的原因是,在
import XXX.YYY.ZZZ
然後
XXX.YYY.ZZZ
可以在表示式中使用。但是
.moduleY
不能在表示式中使用。
相對匯入和__name__
相對匯入使用模組的 `__name__` 屬性來確定該模組在包層次結構中的位置。如果模組的名稱不包含任何包資訊(例如,它被設定為 `__main__`),那麼相對匯入將像模組是頂級模組一樣解析,無論模組實際上位於檔案系統上的何處。
sys.modules中相對匯入和間接條目
當引入包時,sys.modules中出現了間接條目這個概念 [2]。當sys.modules中針對包內模組的條目值為None時,它表示該模組實際引用的是頂級模組。例如,'Sound.Effects.string'在sys.modules中的值可能為None。這意味著任何解析到該名稱的匯入實際上都是要匯入頂級的'string'模組。
這引入了一個最佳化,用於當相對匯入旨在解析為絕對匯入時。但由於本 PEP 對絕對匯入和相對匯入進行了非常明確的劃分,因此不再需要這種最佳化。當絕對/相對匯入成為唯一可用的匯入語義時,將不再支援 sys.modules 中的間接條目。
參考資料
更多背景資訊,請參見以下 python-dev 帖子
版權
本文件已置於公共領域。
來源:https://github.com/python/peps/blob/main/peps/pep-0328.rst
上次修改時間:2025-02-01 08:59:27 GMT