wjohn1483.github.io
  • Posts
  • Archive
  • Audio to Scene
  • Give Feedback
  • About

Python Import Introduction

 
  • Python
  • Nov 15, 2020

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

Reference

  1. Relative imports for the billionth time
  2. The import system
PREVIOUSDeep Reinforcement Learning for Online Advertising in Recommender Systems
NEXT利用Circle CI來更新GitHub Pages
Search