Python Module和Package辨析
Python 基礎學習
說明
- 這不是最基礎的新手教程,如需了解Python的數據類型、變量等基礎內容,請移步:https://docs.python.org/2/tutorial/index.html
- 這裏的代碼使用Python2.7環境,沒有在>3版本號環境下測試,如有不兼容等問題,歡迎交流。郵箱:[email protected]
模塊(Moudule)和包(Package)辨析
- module
通常模塊為一個文件,直接使用import來導入就好了。能夠作為module的文件類型有”.py”、”.pyo”、”.pyc”、”.pyd”、”.so”、”.dll”。 - package
通常包總是一個文件夾,能夠使用import導入包,或者from + import來導入包中的部分模塊。包文件夾下為首的一個文件便是 init.py。然後是一些模塊文件和子文件夾,假如子文件夾中也有 init.py 那麽它就是這個包的子包了。
模塊的使用
以下演示module的使用。包括變量的引用、函數的引用和引用類型的引用。
模塊定義module_demo.py
# 變量
num = 37
#函數
def calc(a, b):
return a + b
#類
class person:
def speak(self):
print "i am a person."
p = person()
模塊使用module_usage.py
import module_demo
print module_demo.num
print module_demo.calc(1, 2)
module_demo.p.speak()
執行結果
37
3
i am a person
到這裏,我們已經了解了模塊的引入和使用。那麽在現實中我們非常可能須要引入多個模塊,應該怎樣做呢?答案是用逗號分隔就能夠了。例如以下所看到的:
模塊使用module_usage.py
import module_demo, module_demo2
...
這裏值得補充的是,引入是能夠使用別名的,使用as關鍵字就能夠了。
模塊使用module_usage.py
import module_demo, module_demo2 as demo
...
假設我們僅僅希望引用模塊中的某個(些)對象呢,我們能夠單獨引入麽?答案是能夠的。使用例如以下:
模塊使用module_usage2.py
from module_demo import calc, p
print calc(1, 2)
p.speak()
從上面的案例中我們發現,我們引入多個對象時,僅僅須要逗號切割就好了。
這裏略微須要註意的是*的使用,比方我們使用from module_demo import *
。我們會以為這是導入模塊中全部的對象。通常情況下確實如此,可是假設該模塊中定義了例如以下內容:
__all__ = [ ‘bar‘, ‘spam‘ ] # 定義使用 `*` 能夠導入的對象
你就得小心註意了。這時候*僅僅代表all所定義的對象。其它的對象不會被導入。
敲黑板 關鍵問題來了,這個import能夠出如今代碼的不論什麽位置,那假設我們多次引入會發生什麽呢?模塊中的代碼*僅僅*在該模塊被首次導入時執行。
後面的import語句僅僅是簡單的創建一個到模塊名字空間的引用而已。
包的使用
多個關系密切的模塊應該組織成一個包,以便於維護和使用。
這項技術能有效避免名字空間沖突。創建一個名字為包名字的文件夾並在該文件夾下創建一個init.py 文件就定義了一個包。你能夠依據須要在該文件夾下存放資源文件、已編譯擴展及子包。
舉例來說,一個包可能有以下結構:
Graphics/
__init__.py
Primitive/
__init__.py
lines.py
fill.py
text.py
...
Graph2d/
__init__.py
plot2d.py
...
Graph3d/
__init__.py
plot3d.py
...
Formats/
__init__.py
gif.py
png.py
tiff.py
jpeg.py
import語句使用以下幾種方式導入包中的模塊:
import Graphics.Primitive.fill #導入模塊Graphics.Primitive.fill,僅僅能以全名訪問模塊屬性,比如 Graphics.Primitive.fill.floodfill(img,x,y,color).
from Graphics.Primitive import fill# 導入模塊fill ,僅僅能以 fill.屬性名這樣的方式訪問模塊屬性,比如 fill.floodfill(img,x,y,color).
from Graphics.Primitive.fill import floodfill #導入模塊fill ,並將函數floodfill放入當前名稱空間,直接訪問被導入的屬性,比如 floodfill(img,x,y,color).
不管一個包的哪個部分被導入, 在文件init.py中的代碼都會執行.這個文件的內容同意為空,只是通常情況下它用來存放包的初始化代碼。導入過程遇到的全部 init.py文件都被執行.因此 import Graphics.Primitive.fill 語句會順序執行 Graphics 和 Primitive 文件夾下的init.py文件.
下邊這個語句具有歧義:
from Graphics.Primitive import *
這個語句的原意圖是想將Graphics.Primitive包下的全部模塊導入到當前的名稱空間.然而,因為不同平臺間文件名稱規則不同(比方大寫和小寫敏感問題), Python不能正確判定哪些模塊要被導入.這個語句僅僅會順序執行 Graphics 和 Primitive 文件夾下的init.py文件. 要解決問題,應該在Primitive文件夾以下的init.py中定義一個名字all的列表,比如:
# Graphics/Primitive/__init__.py
__all__ = ["lines","text","fill",...]
這樣,上邊的語句就能夠導入列表中全部模塊.
以下這個語句僅僅會執行Graphics文件夾下的init.py文件,而不會導入不論什麽模塊:
import Graphics
Graphics.Primitive.fill.floodfill(img,x,y,color) # 失敗!
只是既然 import Graphics 語句會執行 Graphics 文件夾下的 init..py文件,我們就能夠採取以下的手段來解決問題:
# Graphics/__init__.py
import Primitive, Graph2d, Graph3d
# Graphics/Primitive/__init__.py
import lines, fill, text, ...
這樣import Graphics語句就能夠導入全部的子模塊(僅僅能用全名來訪問這些模塊的屬性).
Python Module和Package辨析