Python 中的模塊和包

原文引至:前端小吉米
對於python中的模塊和包, 我簡直就想說, js nmlgb 就是一個 trash... 在前端寫js根本就沒有什麼模塊和包, 所有都是全局... 真lj... css

暢快了. 寫了這麼久的js, 連內部的模塊的包都搞的這麼複雜... 艹...
在python中, 已經定義好了完美的模塊和包的引用機制.
咱們先來看看Modulehtml

Module

在python中, 模塊實際上,就至關於與業務邏輯解耦的可重用的一些函數方法. 因此, 咱們能夠預先定義一個模塊:前端

// 命令爲sam.py
def add(a,b):
    return a+b

以後在同目錄中,打開一個 Terminator.
接着輸入:node

>>> import sam
>>> sam.add(1,2)
//結果爲:
3

實際上, 這就是一個簡單的模塊. 但,當咱們導入的時候, 會在Module的目錄中生成一個.pyc文件, 該其實就是用來對Module進行緩存,並編譯爲Binary 文件, 以便py之後再次引入.
如今, 咱們已經學會如何寫入Module,引入Module. 這裏,咱們須要更進一步去探索, python是如何進行Module Path的搜索的.python

模塊路徑搜索

在nodeJS中, 他的搜索方式是, 先看內部定義的Module 有沒有. 沒有的話,則會開始尋找每一層的node_modules. 而後在裏面搜索你的文件.js
那麼在py中,他是怎樣一個過程呢?
py將搜索路徑,放在了sys.path中.咱們能夠查看一下. sys.path裏面的內容.編程

import sys
sys.path

而後就會出現, 一些路徑,好比這個:緩存

['',
'C:\\Python33\\Lib\\lib-dynload',
'C:\\Windows\\system32\\python33.zip',
'C:\\Python33\\DLLs',
'C:\\Python33\\lib',
'C:\\Python33\\plat-darwin',
'C:\\Python33\\lib\\site-packages']

能夠看到, 第一個爲空, 他實際表明的就是current directory.
具體的順序是:app

  • 當前目錄編程語言

  • sys.path定義的相關目錄ide

  • 安裝依賴的目錄

因此, 若是你想自定義本身的Module 直接添加在sys.path裏面便可.
那應該怎樣進行添加呢?
實際上, 這就是查找關於sys.path的方法了. 還記得help()嗎?
查找後,咱們基本上都會明白了. md, 這不就是list嗎?
那剩下的不就是調用,list的方法進行添加和刪除嗎?
通常而言,python的工做目錄是放在對應的pythonLib文件夾內.這裏,我將我經常使用的py 模塊路徑添加進去

sys.path.append('/Users/jimmy_thr/Documents/pythonLib')

以後, 我只要將我寫好的Module 放在指定的文件內便可.

模塊進階

上面, 咱們只是學會使用基本的Module 導入, 實際上,py提供了 更加豐富的Module statement.
好比, 重命名, 指定導入, 全導入

Module rename

這個算是一個py Module的一個附加值吧.
具體用法很簡單.

import sam as sb
sb.add(1,2)

就醬, 實際上,就是使用as將模塊的名字換一個. 而且, 換了以後sam, 也就不存在了.

Specific Module

直接看demo吧:

//直接導入 add方法
from sam import add
// 也能夠導入多個
from sam import add,minus

這樣導入的結果也是, 不能使用sam.
若是你想, 將module裏面全部的方法都導入的話, 直接使用* 便可

from sam import *

不過, 真強烈不建議這麼作. 由於, 並無什麼卵用, 而且, 萬一出現什麼bug, 都不知道這個方法哪來的.

Module complement

在nodeJS中, 他的模塊引用是值引用類型, 即就是, 一次引用以後, 就會放在緩存裏面, 之後若是在引用的話, 會直接從緩存裏面取了.
看一個demo:

def add(a,b):
    return a+b
print(add(1,2))
//接着,我屢次引用
>>> import sam
3
>>> import sam

只會出現一次, 說明, sam Module 只能導入一次. 其他的就 nonesense. 可是,若是你的模塊是內部循環型的, 那這樣不就go die了嗎?
hehe ~ py 早教看到這一點了, 在內置的imp中提供了reload方法, 來幫助咱們實現, 模塊的引用更新

>>> import imp
>>> imp.reload(sam)
3

最後在補充一個dir(module)方法. 他的做用,就是用來查看指定Module當中的全局變量和函數名.

>>> dir(sam)
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'add']

其他的用underscore來鏈接的, 是py中 module的一部分. 比較重要的看一下__name__便可. 他是用來表示,你當前所在的程序是模塊,仍是主程序.

  • 若是你是主程序則 __name__ 爲 main

  • 若是爲模塊則 __name__ 爲 你的模塊名

另外,若是你忽略參數, 直接使用dir()的話,就是用來查看當前 全局中的變量和函數名.

>>> dir()
['__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', '__warningregistry__', 'get', 'imp', 'sam', 'set']

但,若是你導入一個內置模塊的話, 好比: copy

>>> import copy
>>> dir(copy)
['Error', 'PyStringMap', '_EmptyClass', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', ...]

仔細觀察, 裏面的內置屬性, 會看到多出一個叫__all__的屬性. 這其實和下面所說的包有一些關係. 實際上, __all__ 的做用, 是提供公共接口的, 但實際上, 當咱們導入方法的時候, 每每會所有倒入.好比這樣.

import copy

那麼此時, __all__ 對於這種方法是沒有任何做用, 該語句就是用來導入copy下全部的全局變量.
如今, 假設 當前模塊sam.py下有2個方法,add,minus.
我將__all__ 設置爲

__all__ = ['add']

咱們來試驗一下二者的區別:

//首先導入:
>>> import sam
>>> dir()
['__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'sam']
>>> dir(sam)
['__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'add', 'minus']

能夠看到,有了sam這個模塊名. 而且, 查看sam中,會看到有add和minus方法.
而後,咱們換一種方式導入:

>>> from sam import *
>>> dir()
['__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'add']

咱們會發現, minus不見了.
因此, 這裏咱們就能夠搞懂了__all__ 究竟是用來作什麼的.

  • __all__: 用來設置模塊的公共接口. 而且只針對於 from module import * 的語句

最後在說一下,其中的另外兩個默認屬性 __doc__,__file__

  • __doc__: 實際上,就是咱們使用help()函數查看的內容. 內容具體就是咱們寫在模塊開頭或者函數開頭的註釋

  • __file__: 模塊真實的文件路徑, 若是你想查看源碼的時候就很重要了.

放圖:
python Module

Package

說完了,py中的Module, 接着就是最讓你66的包. Module只是幫助咱們完成了一個組件的一些小功能, 可是若是咱們想要寫一個能夠調用的總體組件的話, 那麼一個single Module顯然是不夠了. 因此,py 推出了package 來幫助咱們完成這個巨大的工程(project). 咱們能夠經過個文件,調用,該組件下全部的方法. 好比,咱們須要寫一個html的評論框, 那麼裏面確定會設計, HTML,css,js樣式設計, 接收評論,發佈評論等不少功能. 在包裏面, 咱們就能夠把這些小功能進行拆分,達到複用的效果.
先看張圖:
python 包
這實際上, 就是咱們python包的簡單格式, 在每一個文件根目錄都會存在__init.py__ 文件. 他的做用實際上,就是用來定義, 引用包時, 暴露的相關接口. 而關鍵的關鍵, 就是上面提到的__all__ 內置的默認關鍵字. how to use?
請, stackoverflow.
__init__.py就是一個導入文件
如今,咱們來寫一個簡單的包, 以上圖爲例。
在Game的根目錄下, __init__.py內容爲:

__all__ = ['Sound','Image','Level']
from Game import Sound,Image,Level

而後,咱們就能夠直接應用Game 包了.

>>>import Game
>>>Game.Sound.xxx

這裏,須要說明的是, 關於包的導入, 其實用不用all不是很重要, 換句話說, 應該是不推薦, 由於前文咱們已經瞭解到, __all__ 生效的機制是 使用 from xx import * 這樣的語句. 而這樣作的實際效果是, 徹底破壞了python的namespace機制, 也是編程語言中最重要的一個. 因此, 給的建議就是, 儘可能放棄all的使用, 直接使用 import 來判斷你須要導出那些公共的接口便可.
引用一段話:

Leaving an __init__.py file empty is considered normal and even a good practice, if the package’s modules and sub-packages do not need to share any code.

出自: python guider

相關文章
相關標籤/搜索