在寫python時,常搞不懂import路徑的寫法,這邊簡單介紹一下python import的邏輯。
Script v.s. Module
執行python檔案有兩種方式:
- 把該檔案視為top-level script來執行:
python main.py - 把它當作module執行:在其他檔案裡面透過
import來執行,或是python -m main.py
當一個python檔案被執行的時候,它會被給予一個名字,儲存在__name__這個變數裡面,如果它被當作top-level script執行,__name__就會被指定成__main__,如果被當作module執行,__name__會被指定成該檔案的名字加上packge的路徑。
值得一提的是,雖然
python -m main.py是把main.py當作module來執行,但印出來的__name__仍然是__main__,不過在底下執行relative import是沒有問題的。
舉例來說,如果我們當下的目錄結構長得像底下這樣
.
├── main.py
└── package
└── moduleA.py
當我們下python main.py或是python package/moduleA.py時,他們的__name__都是__main__,如果我們在main.py裡面import package.moduleA,並在moduleA.py裡面把__name__印出來,會發現印出來的__name__會是package.moduleA。
值得一提的是,如果你今天是直接打python進入interactive shell,那麼當下shell的__name__就會是__main__。
Relative Import
relative import會透過module的名字來去決定package的位置,假設我們現在稍微修改一下目錄的架構變成底下
.
├── main.py
└── package
├── moduleA.py
└── subpackage
└── moduleB.py
假如說我們用import的方式在當前目錄執行moduleB.py(from package.subpackage import moduleB),那麼當前的__name__就會是package.subpackage.moduleB,而我們在moduleB.py裡面有from .. import moduleA,這時python就會去package裡面尋找moduleA,就可以順利的import進來。
但如果今天是用直接執行的方式python package/subpackage/moduleB.py,會因為__name__為__main__而找不到parent package,噴底下的error。
Traceback (most recent call last):
File "package/subpackage/moduleB.py", line 1, in <module>
from .. import moduleA
ImportError: attempted relative import with no known parent package
這時可以改用python -m package.subpackage.moduleB來把moduleB.py當作module執行。
Absolute Import
根據當前執行目錄的絕對路徑來import module,值得一提的是,當前執行的目錄並不是你執行python時所在的目錄,而是top-level script所在的目錄。
.
├── main.py
└── package
├── moduleA.py
└── subpackage
└── moduleB.py
以上面的結構來說,若我在當前目錄./執行python package/moduleA.py,那麼若要在moduleA.py裡面import moduleB,就得要用from subpackage import moduleB,而不是from package.subpackage import moduleB,因為moduleA.py在./package裡面,python會從那邊起始點。
__init__.py的用途
在上面的例子當中,我們都沒有使用到__init__.py,但應該在很多地方都可以看到他的蹤影,而__init__.py的作用是將一個目錄轉變成package。
舉例來說,在上面的例子裡面,我們將__init__.py放到package/底下,這時package就變成了一個regular package。
Regular Packages
在python裡面主要有兩種package,分別是regular package和namespace package,而regular package就如同上面所說的,只要有__init__.py在目錄裡面的,就是regular package。當我們import regular package的時候,__init__.py會被執行,也因此我們可以在__init__.py裡面做一些手腳,讓這個package變得更易於使用。
Namespace Packages
倘若我們不放__init__.py在目錄裡面,在python 3.3以後,我們依然可以import package,就像上面absolute import的章節中做的一樣,但不一樣的是,被import進來的package會被當作namespace package。Namespace package的用途主要是讓你可以切分多個sub-package到不同的目錄當中,但仍屬於同樣的namespace,以方便使用者import,舉例來說,我們在regular package的方式下做好了一個package如下。
mynamespace/
__init__.py
subpackage_a/
__init__.py
...
subpackage_b/
__init__.py
...
module_b.py
setup.py
而我們import他們的方式會是
from mynamespace import subpackage_a
from mynamespace import subpackage_b
但某些時候我們可能只需要subpackage_a就行,不想要額外安裝subpackage_b,這時可以將mynamespace拆成兩個package,拆成兩個package的方法可以參考官方文件。
mynamespace-subpackage-a/
setup.py
mynamespace/
subpackage_a/
__init__.py
mynamespace-subpackage-b/
setup.py
mynamespace/
subpackage_b/
__init__.py
module_b.py
使用者就可以根據自己的需求pip install相對應的package,而這兩個package在安裝完以後都會同屬於mynamespace這個namespace底下,因此一樣可以用from mynamespace import subpackage_a, subpackage_b來import。
值得一提的是,如果import.zip檔,一樣會被當作是namespace package。
In [1]: import sys
In [2]: sys.path.append("./module_zip.zip")
In [3]: import module_zip
In [4]: module_zip.__path__
Out[4]: _NamespacePath(['./module_zip.zip/module_zip'])
In [5]: module_zip
Out[5]: <module 'module_zip' (namespace)>