在寫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)>