Python的包導入機制

Background

在Python的大型項目中,一般都會用到模組包來組織文件層次,其中當一個目錄內含有__init__ . py文件時,就可以視該目錄為一個模組包。 當在模組包中使用import語句的時候,不同的語法會導致不同的模組搜索導入方式,常見的導入方式如下:

  • 絕對導入(absolute import)
  • 顯式相對導入(explicit relative import)
  • 隱式相對導入(implicit relative import)

需要注意的是,這些導入方式都是對於模組包而言,對於一般的模組還是從sys.path搜索入手。

Prerequisite

  • Python腳本運行的方式?
    • Python腳本運行分為兩種方式:一種是作為top level script運行,另一種則是作為被導入的包模組運行。
    • 當使用python命令直接執行一個py文件的時候,該文件就是以top level script方式運行,此時文件的__name__屬性則為__main__。 # test.py print __name__ # output __main__
    • 當文件使用包模組的方式運行的時候,文件的__name__屬性則為模組的路徑(從top level script的目錄開始),包模組的例子目錄結構如下: ├── main.py └── pac ├── __init__.py └── moduleA.py 筆者將直接運行main.py文件,程式碼如下: ## main.py from pac import moduleA ## moduleA.py print __name__ ## output pac.moduleA
    • 這兩種運行方式還有不同的地方是,當使用top level script的方式運行的時候是不會生成位元組碼的(即.pyc文件),而通過包模組的方式則會生成位元組碼。

Relative Import And Absolute Import

假設如下的import語句:

import string

這個string是當前目錄下的string模組呢,還是在標準庫的string模組呢?在早期的Python中,當使用import語句的時候,都會優先尋找目錄內的模組,因此這就是隱式相對導入。

但是在有同名模組的情況下,如果還想引用標準庫中的string模組那該怎麼辦?因此Python實現了絕對導入,在絕對導入的模式下,當使用import string的時候,就會優先搜索當前目錄以外的模組。絕對導入模式是Python3默認採取的包導入方式,其實這種方式在Python2.5及以上版本就已經實現,要想使用只需加上:

from __future__ import absolute_import

關於隱式相對導入於絕對導入的例子如下: 包結構:

├── main.py  └── pac      ├── __init__.py      ├── __init__.pyc      ├── explicit_import.py      ├── explicit_import.pyc      ├── implicit_import.py      ├── implicit_import.pyc      ├── string.py      └── string.pyc

程式碼如下:

# main.py  from pac import implicit_import  from pac import explicit_import    # explicit_import.py  from __future__ import absolute_import    import string  print string.digits    # implicit_import.py  import string  print string.digits    # string.py  digits = '2333'    ## output  2333(relative import)  0123456789(absolute import)

絕對導入還有一種使用方法,比如在explicit_import.py中可以通過:

from pac.implicit_import import *

來引用implicit_import文件中的變數。

explicit relative import

雖然絕對導入能夠完成相對導入的所有功能,但是顯式的相對導入也是可以接受的。當使用.語法的時候就是使用相對導入:

# 導入當前目錄下的string模組  # right  from . import string  # wrong  import .string 

至於下面的導入方法錯誤的原因,這是因為Python語法不支援的緣故。 同時值得注意的是,顯式的相對導入是根據模組的__name__屬性來確定相對位置的,因此如果是在top level script中,顯式相對導入並不能使用,會報出如下錯誤:

ValueError: Attempted relative import in non-package

當然,在PEP 366 – Main module explicit relative imports中,也給出了在Python中執行非包內的模組(作為top level腳本執行)使用顯示相對導入的方法:在執行python命令時加上-m選項,此時就會啟用模組的__package__屬性。

詳細的關於相對導入與絕對導入參考:PEP 328 – Imports: Multi-Line and Absolute/Relative