Python 中 import module 和 package 方法簡單記錄


  Python 中以 .py 結尾的文件實際上為一系列 Python 語句的集合,其被稱為 Python 腳本(scripts),當用戶通過 python test.py 命令運行該腳本時,實際上是由 Python 解釋器從上至下逐行對其中的語句進行執行。對於大多數 Python 腳本而言,在開始位置一般通過 import 語句來導入其所需要的模塊。這里記錄一下 Python 模塊和其導入相關的內容,供學習和討論。主要內容來自 The Python Tutorial -> 6.Modules.

 

  module

  在 Python 中,可以將一系列的函數或變量定義放在一個 .py 文件中,供其他文件進行使用,這樣的 .py 文件稱為一個模塊(module). 模塊的名字即為 Python 文件名,如 test.py 即對應 test 模塊,在模塊內部,可以通過變量 __name__ 來對模塊名進行訪問。通過 import 關鍵字,在 Python 腳本中導入該模塊相關名字,后續可通過導入的名字進行調用。兩種常見的導入模塊中名字的方式如下所示。在一個 Python 解釋器的運行過程(session)中,每個模塊只會被導入一次。在導入過程中,模塊中的內容會按照從上到下的順序進行執行和解析。

    import test        #導入 test 模塊,此時將名字 test 引入當前的符號表中(從而可以引用)
    test.doSomething()    #通過 test 模塊名使用其中定義的函數 doSomething

    from test import doSomething   #從 test 中引入 doSomething,注意此時 doSomething 被加入當前符號表(可直接引用),而 test 則不會被加入(無法直接引用)
    doSomething()

    from test import *    #從 test 中導入所有的名字,除了以 '_' 開始的名字

  模塊作為一個 .py 文件,也可以作為腳本文件使用,當使用 Python test.py 的方式運行模塊時,其模塊名 __name__ 會被填充為 "__main__".

  用戶可以通過內置的 dir() 函數查看模塊中定義的名字,其一般有兩種用法:

    dir(test)    #查看 test 模塊中定義的所有名字
    dir()        #查看當前已定義的所有名字,注意不包括內置定義的名字

    import builtins
    dir(builtins)    #查看所有內置函數和變量的名字

 

  module 的搜索順序

  當通過 import 語句導入一個模塊的名字時,Python 解釋器遵循一定的搜索順序,其基本順序為:

  1)首先搜索 Python 自帶的模塊;

  2)在 sys.path 變量規定的一系列目錄中尋找名為 模塊名.py 的模塊文件。sys.path 一般被初始化為 a) 包含當前運行腳本的當前目錄;b)PythonPATH 宏定義的路徑;c)一般還包括 site-packages 目錄,由 site 模塊處理;

  sys.path 的相關內容可以參考 sys.path.

 

  package

  Python 的包(package)可以被理解為一系列模塊的集合。每個 Python 包需要包含有一個對應的 __init__.py 文件,__init__.py 文件可以為空,也可以用於執行 Python 包導入時的初始化等操作。

    test/    #一個 test 包的定義
    __init__.py #必須包含有 __init__.py 文件
    t1.py    #包中的模塊
    t2.py

  當執行 package 的 import 時,Python 解釋器通過 sys.path 指定的路徑來搜索包的路徑。用戶通過正常的 import 操作導入包中的名字。

    import test.t1    #導入名字 test.p1,后續通過該名字進行引用
    from test import t1 #同上,但導入的名字為 p1

  不同的 import 語句對於 import 對象有不同的要求。

    from package1 import test #將名字 test 視為定義在包中的名字(如包中定義的函數和變量等)進行定位,若無法定位,則將其視為一個模塊進行加載,若加載失敗,則會拋出 ImportError 異常
  import package1.package2.test #語句要求除了最后一個名字 test 外,前面所有的名字均需要為 Python 包,最后一個名字可以為一個模塊或一個包,但不能為定義的函數或變量。

  當通過 from test import * 語句導入包中的模塊時,若 __init__.py 中定義了 __all__ 變量,則 __all__ 變量中包含有的模塊名會被導入,若沒有定義 __all__ 變量,則上述導入語句不會導入 test 包的任何子模塊,其僅將包名 test 導入當前符號表,同時會導入包中定義的變量和函數等名字(如__init__.py 文件中包含的名字)

  當 Python 包被組織為目錄結構時,可以通過決定路徑或相對路徑來進行 import.相對路徑指定從 package 開始到具體模塊名的路徑,而相對路徑以 "." 開始,相對於當前模塊位置來進行路徑搜索。

    from test import t1    #當 test 可以通過 sys.path 定位時,可通過絕對路徑進行導入
    from . import t2    #在模塊 t1 中可以通過相對路徑對 t2 進行導入

  

  Python 腳本的編譯

  為了提升 Python 解釋器加載模塊的速度,模塊的定義通常被編譯並緩存,編譯好的模塊文件位於目錄 __pycache__ 目錄下,並以 module.version.pyc 的格式進行命名,module 對應模塊名,verison 對應編譯信息,.pyc 為對應的后綴。Python 通過檢查源文件的修改日期和編譯的 version 信息來決定是否需要進行重新編譯。

 

  問題

  當存在當前一個目錄結構的 python 工程時,如何從 main.py 中調用 func.py 中定義的函數?

    src/ #源目錄
        test/
            func.py #定義了一些函數
        main.py #執行腳本

  1.對於 Python 3.3+ 的解釋器版本,可以直接在 main.py 中使用 import 語句,即 from test import func 語句可直接導入 func 模塊,進而使用 func 名字來使用 func.py 中定義的函數等內容。這是由於 Python 3.3+ 中允許將不包含 __init__.py 文件的目錄視為一個包,其被稱為 namespace packages. 這種定義包的方式一般用於 namespace 的共享,不推薦平常使用。具體可參考:Is __init__.py not required for packages in Python 3.3+

  2.由於 module 的搜索路徑中包含有 sys.path 中定義的路徑,故而可以將 test 目錄加入 sys.path 路徑中,從而使得解釋器在搜索模塊時可以直接定位 func.py. 即通過 sys.path.insert(0, './test/'); import func 即可。但該方法一般比較使用於小項目的構建和內部使用的 trick;

  3.將 test 作為一個包進行處理。在 Python 中,包含有 __init__.py 文件的目錄即被視為一個 Python 包,之后即可通過對包中的模塊導入方式進行引用。故而可以在 test/ 目錄下加入 __init__.py 文件,此時 test 被視為一個 Python 包,可以通過 import test.func as func 或者 from test import func 來使用 func 模塊中定義的內容;

 

  參考

  IMPORTERROR: ATTEMPTED RELATIVE IMPORT WITH NO KNOWN PARENT PACKAGE

  Is __init__.py not required for packages in Python 3.3+

  關於 import module 和 from module import 可以參考:Use 'import module' or 'from module import'?


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM