Python 中包/模組的 `import` 操作
Python 中包/模組的import
操作
版權宣告:部落格為作者原創,允許轉載,但必須註明原文地址:https://www.cnblogs.com/byronxie/p/10745292.html
建立以下包結構。一個資料夾cookFish/
,下面包含兩個檔案,__init__.py
和cookBook.py
。
為什麼取這幾個名字呢?假設我想用 Python 去做和魚相關的菜,這件事情很複雜,所以我給它建立了一個包,名叫cookFish
, 既然是包,在它下面必須得建立一個檔案__init__.py
。燒魚必備條件之一就是菜譜,所以接著建立了cookBook.py
。這幾個檔案對我們這次來說就足夠了,所以就沒有再建立其他檔案了。
cookFish/ __init__.py cookBook.py
在cookFish/__init__.py
中輸入如下程式碼:
__version__ = '0.1' __author__ = 'XIE Byron' def cookFish_hello(): print("cookFish_Hello() from cookFish/__init__.py")
在cookFish/cookBook.py
中輸入如下程式碼:
def cookBook_hello(): print("cookBook_hello() from cookBook.py")
"import package-name" 都做了什麼?
匯入包cookFish
。
>>> import cookFish
提示:
如果import
時出現錯誤ModuleNotFoundError
,如下:
--------------------------------------------------------------------------- ModuleNotFoundErrorTraceback (most recent call last) <ipython-input-11-18f0abd953db> in <module> ----> 1 import cookFish ModuleNotFoundError: No module named 'cookFish'
建議先將 Python 的當前工作目錄
設定為cookFish
的父資料夾
(就是包含cookFish
資料夾的資料夾)。命令如下:
>>> import os >>> os.chdir(r'path\to\parent\folder\of\cookFish')
用dir
操作檢視當前名稱空間
和
cookFish
名稱空間
下都有哪些內容。
>>> dir() # 檢視當前名稱空間下的物件。注意: cookFish 在當前名稱空間下。 ['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'cookFish', 'os'] >>> dir(cookFish) # 檢視 cookFish 名稱空間下的物件。 ['__author__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', '__version__', 'cookFish_hello']
其中的的__author__
,__version__
,cookFish_hello
是我們定義的,都匯入到了cookFish
的名稱空間下。但是cookFish
下的模組cookBook.py
沒有被匯入。這是因為直接import cookFish
只執行cookFish
資料夾下的__init__.py
檔案,不會執行其他模組,所以cookBook
沒有被匯入。
提示:Python 中的模組
指字尾.py
的檔案,也叫指令碼
。包
指包含__init__.py
檔案的一個資料夾,一般還會包含其他模組。
包/模組的名稱空間
這裡講一下我對概念
“在cookFish
的名稱空間下”
的理解。
Python 的import A
會把A
的Python 程式碼執行一遍,並把執行結果放在一個叫A
的名稱空間下。
提示: 如果A
是包,A
的 Python 程式碼就是 資料夾A下的__init__.py
中的程式碼。 如果A
是模組,那麼就是檔案A.py
中的程式碼。
import B
會把B
的 Python 程式碼執行一遍,並把執行結果放在一個叫B
的名稱空間下。假設A
和B
中都有一個叫X
的物件,A
中的X
在當前名稱空間下叫A.X
,B
中的X
在當前名稱空間下叫B.X
,兩個X
在當前名稱空間下不重名。
提示: 這裡的物件
指 Python 中的變數/屬性,函式,類,例項等等。
比如__version__
屬性(或者叫它變數)就在cookFish
的名稱空間下,我們只能通過cookFish.__version__
才能訪問到__version__
,直接輸入__version__
訪問不到它,會報錯。
直接輸入__version__
執行會報如下錯誤:
--------------------------------------------------------------------------- NameErrorTraceback (most recent call last) <ipython-input-4-8d40cf051c04> in <module> ----> 1 __verison__ NameError: name '__verison__' is not defined
其他匯入包/模組的方式
如果我們想匯入cookFish
下的模組cookBook
呢?可以用下面的語法:
>>> import cookFish.cookBook
然後就能通過全名cookFish.cookBook
訪問cookBook.py
中的物件了,比如:
>>> cookFish.cookBook.cookBook_hello()
好長的名字啊,能不能短一點啊?當然可以
>>> import cookFish.cookBook as cb
然後就能通過別名cb
來訪問cookBook.py
中的物件了,比如:
>>> cb.cookBook_hello()
那我能不能只匯入cookBook_hello()
到當前名稱空間?當然可以
from cookFish.cookBook import cookBook_hello
然後就能直接訪問cookBook_hello()
了,不用任何字首:
>>> cookBook_hello()
“from 包/模組名 import *” 是匯入所有物件嗎?
那我可以一次性匯入cookFish
下的所有模組、所有包嗎?可以也不可以。
Python 有一個條指令
from 包/模組名 import *
比如from cookFish import *
,給我們的第一感覺是,這條指令是遍歷了cookFish
下的所有檔案,找到這個包下面的所有包和模組,把他們統統匯入到當前名稱空間。
但不幸的是,這個操作在windows和Mac系統上不能很好地實現。因為它們的檔案系統不能提供準確的檔名大小寫資訊。在這兩個平臺上,Python 不知道應該把ECHO.py
匯入為模組echo
,Echo
還是ECHO
,或者其他。(比如windows 95 上面,所有檔名的首字母都會顯示為大寫)。如果Python 把ECHO.py
匯入為 模組Echo
,但實際Python程式碼中有時按照echo
使用的,那肯定會報錯。[1]
Python 支援大小寫,Echo
和ECHO
是兩個不一樣的物件
Python 的唯一的解決辦法是包的作者提供一個明確的包的索引,告訴 Python 在 Python 程式碼中如何命名這個模組。import 語句定義下面一個約定,如果在包的__init__.py
中定義了一個__all__
列表,在from xxx import *
時,Python 就會把__all__
列表中的物件匯入。
! 注意:
__all__
只對from xxx import *
有影響,對其他 import 操作沒有任何影響
在cookFish/__init__.py
中, 我們只把函式cookFish_hello
加入__all__
中,程式碼如下:
__all__ = ['cookFish_hello', ] # added to support `from xxx import *` __version__ = '0.1' __author__ = 'XIE Byron' def cookFish_hello(): print("cookFish_Hello() from cookFish/__init__.py")
重啟Python 直譯器,執行如下:
>>> from cookFish import *
! 注意:
Python 直譯器為了提高執行效率,同一個模組只會匯入一次。一個模組被匯入後,再次執行匯入命名不會重新匯入。所以需要重啟 Python 直譯器(就是關閉 Python 直譯器,然後重新進入)。
然後輸入dir()
檢視cookFish_Hello()
是否被匯入到了當前名稱空間.
>>> dir() ['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'cookFish_hello', 'os']
可以看到只有在__all__
列表中的cookFish_hello
被匯入到當前名稱空間。
所以問題“可以一次性匯入cookFish
下的所有模組、所有包嗎?“ 的答案是:是否能一次匯入,取決於包的作者有沒有把所有子模組/子包都加入到__all__
列表中。