當直接run某個.py文件,而這個.py文件中有諸如:
from . import x
from .. import y
的導入語句時,就會報上述相對路徑錯誤。
根源可歸結為下面一句話:
Relative imports use a module’s name attribute to determine that module’s position in the package hierarchy.
也就是說,相對路徑是根據(jù)當前module的名稱屬性來決定所導入的相對模塊的位置的。
首先我們回顧一下絕對導入(即導入時的module名不以.開頭)的搜尋路徑(python 2.7文檔6.1.2 The Module Search Path有詳細闡述)。
1、built-in模塊
2、在sys.path指定的目錄中查找。sys.path在啟動python解釋器時按照下面的順序初始化
a、當前運行腳本所在的目錄
b、PYTHONPATH(即標準庫,類似于'C:\\WINDOWS\\SYSTEM32\\python27.zip', 'C:\\Python27\\DLLs', 'C:\\Python27\\lib', 'C:\\Python27\\lib\\plat-win', 'C:\\Python27\\lib\\lib-tk', 'C:\\Python27',)
c、安裝依賴的一些缺省值(類似于C:\\Python27\\lib\\site-packages)
你可以在代碼中修改sys.path的值來改變搜尋路徑,比如sys.path.append( "/specified/path"),當然也許你想將這個特定的搜尋路徑放在sys.path的開頭,這樣具有更高的優(yōu)先級,可以這樣做sys.path.insert(0, "/specified/path")。
話說回來,相對導入是根據(jù)當前module的名稱屬性來決定所導入的相對模塊的位置的,也就是根據(jù)當前module的__name__屬性來計算相對導入模塊的位置,如果你直接運行module.py(a.k.a python module.py),那么當前module.py的__name__屬性值為"__main__"(比如你經(jīng)常在其中添加 if __name__ == "__main__": do_something()),顯然根據(jù)"__main__"來計算相對路徑肯定得不到你想要的結果。
假設如下的代碼層次結構:
package/
? ? __init__.py
? ? subpackage1/
? ? ? ? __init__.py
? ? ? ? moduleX.py(from .. import moduleA)
? ? moduleA.py
run.py (import package.subpackage1.moduleX)
如果python run.py,則運行到moduleX中時,moduleX的__name__屬性值就是package.subpackage1.moduleX,這時根據(jù)導入,會得到package.subpackage1.moduleX..moduleA,即package.subpackage1.moduleA也是準確的。
簡而言之,當你運行某個python文件時,這個python腳本最好處于你的代碼的頂部(top-level),且該python腳本都采用絕對路徑導入(因為該python腳本的__name__此時是"__main__",仍無法使用相對路徑)。在底層的目錄中的python腳本就可以使用.開頭的相對路徑了。但要注意的是,為了采用相對路徑能找到對應的module,目錄中必須有__init__.py,這才會構造成一個package,即便__init__.py是個空文件!
一個繞過上述規(guī)則的方法(https://www.python.org/dev/peps/pep-0366/): __package__. When it is present, relative imports will be based on this attribute rather than the module __name__ attribute。你可以在代碼中這樣寫:
if __name__ == "__main__" and __package__ is None:
? ? __package__ = "expected.package.name"
即明確為module指所屬的package名稱。
參考:
1、https://blog.csdn.net/qiusuoxiaozi/article/details/79061885
2、https://www.python.org/dev/peps/pep-0366/