Python 2.7 學習筆記 模塊和包


我們來考慮下如下幾種場景:

1、編寫一個python程序,如果程序比較簡單,則可以把代碼放到一個python文件中。但如果程序功能比較多,可能需要多個python文件來組織源代碼。而這些文件之間的代碼肯定有關聯,比如一個文件中的python代碼調用另一個python文件中定義的函數。

2、我們編寫程序,肯定不會所有的東西都自己寫,不會全部重造輪子,我們肯定會用到Python提供的一些標准庫。那怎么使用呢?其實前面的文章中已經看到了,用import語句。 如同java中用import,c#中用using語句。

3、我們自想編寫一個公共代碼,或從外部找到一個第三方的公共代碼,如何放入到整個python系統中,如何被自己編寫的代碼使用。

上面這些場景,都是在編寫程序時常見的事情。

這些問題,python是通過模塊和包的機制來解決的。

簡單的說,一個模塊就是一個python文件,一個包是包含一組模塊。下面我們通過實際的例子來說明。

一、案例1:一個最簡單例子

編寫 test1.py文件,代碼如下 

#coding=utf-8
import test2
print "hello"
test2.fun("world")

編寫test2.py文件,代碼如下

#coding=utf-8
def fun(para):
    print para

這兩個python文件位於同一目錄,但不一定要在python的相關系統目錄下,可以是任意的合法目錄。

這時我們執行 test1.py,可以成功運行。
可以看出,test1.py中的代碼 調用了 test2.py中的 fun方法,這能夠調用的關鍵是在test1.py中 import test2這個語句,表示將test2.py這個模塊引入進來,

同時調用時是通過  模塊名.函數名 調用的。

 

二、案例2: 如何放置模塊

上面的例子,是兩個python文件位於同一目錄下。如果test2.py想放在其它目錄下怎么辦呢?

這里關鍵是讓python解釋器能找到 test2.py。

這種情況很常見,比如 test2.py是個通用的模塊,可以被多個程序使用,那它就不能與使用它的程序放在一起,否則就要拷貝多份了。

將test2.py 放在其它地方,有多種方法,下面分別介紹。

方法一:放在python已有的系統目錄下

把模塊(python文件)放在python的系統目錄下,引入模塊時,python解釋器就能找到。

可以通過如下的代碼查看當前有哪些系統目錄:

>>> import sys,pprint
>>> pprint.pprint(sys.path)
['',
 'C:\\Python27\\lib\\site-packages\\pip-7.1.2-py2.7.egg',
 'C:\\Python27\\lib\\site-packages\\paramiko-1.15.2-py2.7.egg',
 'C:\\Python27\\lib\\site-packages\\robotframework_sshlibrary-2.1.1-py2.7.egg',
 'C:\\Python27\\lib\\site-packages\\ecdsa-0.13-py2.7.egg',
 'C:\\Python27\\lib\\site-packages\\selenium-2.47.1-py2.7.egg',
 'C:\\Python27\\lib\\site-packages\\decorator-4.0.2-py2.7.egg',
 'C:\\Python27\\lib\\site-packages\\easyprocess-0.1.9-py2.7.egg',
 'C:\\Python27\\lib\\site-packages\\webtest-2.0.20-py2.7.egg',
 'C:\\Python27\\lib\\site-packages\\beautifulsoup4-4.4.1-py2.7.egg',
 'C:\\Python27\\lib\\site-packages\\waitress-0.8.10-py2.7.egg',
 'C:\\Python27\\lib\\site-packages\\webob-1.5.1-py2.7.egg',
 'C:\\Python27\\lib\\site-packages\\six-1.10.0-py2.7.egg',
 'C:\\Python27\\lib\\site-packages\\jsonpointer-1.10-py2.7.egg',
 'C:\\Python27\\lib\\site-packages\\jsonpatch-1.12-py2.7.egg',
 'C:\\Windows\\system32\\python27.zip',
 'C:\\Python27\\DLLs',
 'C:\\Python27\\lib',
 'C:\\Python27\\lib\\plat-win',
 'C:\\Python27\\lib\\lib-tk',
 'C:\\Python27',
 'C:\\Python27\\lib\\site-packages',
 'C:\\Python27\\lib\\site-packages\\win32',
 'C:\\Python27\\lib\\site-packages\\win32\\lib',
 'C:\\Python27\\lib\\site-packages\\Pythonwin']

python的 標准庫 sys模塊中的path對象包含了所有的系統路徑,利用 pprint模塊中的pprint方法可以格式化的顯示數據,如果用內置語句print則只能在一行顯示所有內容,查看不方便。
我們只要把python文件(如本文例子中的test2.py)放在上述任何目錄下,python解釋器就能找到。

注意:必須直接放在上述目錄下,不能建立子目錄,放在子目錄下。要想能放到子目錄下,就是包的概念,下面會介紹。

方法二:新增系統目錄

除了python自己默認的一些系統目錄外,應用程序也可以通過代碼添加系統目錄。

因為系統路徑是存在 sys.path對象下的,path對象是個列表,就可以自己通過代碼往其中插入目錄,如

sys.path.append("D:/demo/python/dir")

但很顯然,插入的這個目錄作為系統目錄只能對當前程序生效,因為這只是在內存中生效。

方法三:設置環境變量

如果我們不想把代碼放在python的系統目錄下,以免和python的目錄混在一起,增加管理的復雜性。

甚至有的時候,因為權限的原因,還不能在python的系統目錄下加文件。

而希望放在自己規划的目錄下。 這時就可以操作系統的 PYTHONPATH 環境變量,該環境變量包含一系列的目錄。

該環境變量下的所有目錄都能被python解釋器搜到。

這樣我們就可以將代碼放到PYTHONPATH 環境變量包含的目錄下(注意不能是子目錄,除非是包),就可以被別的程序import了。

設置PYTHONPATH 環境變量是相對比較好的方式,推薦使用。

 

案例三:路徑的優先級

根據上面的介紹,一個模塊要能別的程序引用(import)到,可以和程序放在一個目錄,可以放到python系統目錄下,可以放到PYTHONPATH 環境變量包含的目錄下,那哪個優先級最高。

這時我們可以測試下,寫三個同名文件,文件中定義同名的函數,函數只有一個print語句,但三個文件中的函數的print語句內容不同。

再編寫另外一個程序import上面這個文件,並調用定義的函數,看看輸出,就知道優先級了。

經過測試,發現優先級從高到底分別是:

1)當前目錄

2)環境變量PYTHONPATH包含的路徑

3)python系統目錄

這個其實也很好理解,正常情況,越是用戶的設置優先級越高。

 

案例四:包

我們上面的介紹,每個模塊都是獨立的一個python文件。為了讓python能發現他們,必須放在相應的目錄下。

沒有分層,容易造成命名沖突和管理上的混亂。

特別在實際情況下,一個功能往往由多個模塊(文件)組成,一般我們希望把這些代碼放在一個目錄下,便於管理。

這就要用到python的包的機制了。

python的包,物理上是一個目錄,它實際上也是一個模塊,只是比較特殊的模塊,就是它還能包含其它模塊。

下面我們舉例來說明:

我們創建一個目錄,如 testpackage

要想這個目錄成為一個python包,而不是一個普通的目錄,關鍵是在該目錄下創建一個文件 __init__.py , init的前后分別是兩個連續的下划線。

__init__.py文件名是固定的,但其中內容是任意的,就如同編寫一個模塊一樣,可以放置任意的代碼。如:

#coding=utf-8
print "hello,i am package"
def hello():
    print "good"

我們再在testpackage目錄 所在的目錄下建立一個test.py文件,內容如下

#coding=utf-8
import testpackage
testpackage.hello()

test.py中就導入了testpackage,這時我們執行test.py,發現輸出:
hello,i am package
good

可以看出,testpackage就是一個特殊的模塊,但因為它本身不是一個python文件,而是一個目錄,那么它下面的__init__.py 就是模塊的內容,導入包,其實就是導入__init__.py文件。 而普通模塊對應的是python文件,要求模塊名和文件名一致。

我們再在 testpackage 下建立兩個文件,module1.py , module2.py ,內容分別是:

#coding=utf-8
def fun1():
    print "module1"
#coding=utf-8
def fun2():
    print "module2"

下面我們來使用這兩個模塊,修改test.py文件。修改后的test.py的內容如下:

#coding=utf-8
import testpackage
import testpackage.module1
from testpackage import module2
testpackage.hello()
testpackage.module1.fun1()
module2.fun2()

執行test.py的輸出如下:
hello,i am package
good
module1
module2

下面我們來分析下test.py中的內容

可以看出,我們用兩種不同的方式導入了module1模塊和 module2模塊。

采用import testpackage.module1 方式,則要求在使用module1中的函數等時,需要全路徑引用,如testpackage.module1.fun1()。

采用from testpackage import module2方式,則要求在使用module2中的函數等時,可以省略包名引用,如module2.fun2()。

一般情況下,我們采用from導入的方式。

還有一點需要說明的是,導入包中的模塊,這時就不再需要導入包,因為會自動先導入模塊所在的包,也就是說會自動導入包的__init__.py文件。

另外一點,既然包是一個特殊的模塊,它的存放和普通模塊一樣,可以和用它的程序在一個目錄下,可以在python系統目錄下,也可以放到環境變量PYTHONPATH包含的目錄下。

有了包這個功能,對於復雜的程序,就可以更好的組織源代碼。

 

案例五:模塊中能放什么呢?

在前面的例子中,已經涉及到了一些內容,下面我們再來更為詳細的介紹下模塊中能放什么,怎么用的問題。

第一,模塊中能放什么,理論上說,跟普通的python代碼文件一樣,可以放 變量、函數定義、類定義,甚至直接的語句調用等內容。

第二,用的問題。首先就要被導入。

被導入時,模塊中直接寫的語句,如 print就會被立即執行,變量等也會被定義和初始化(如果有的話)。

 

第三,一個python文件,既可以作為作為主程序直接被執行,也可以作為一個模塊被其它程序(或模塊)導入。

那有的時候,我們希望有些直接寫在文件最頂層的代碼(不是函數或類)在作為程序直接執行 和 作為模塊導入時是有差別的,那該怎么辦?

我們還是看例子。

假設有 test1.py文件,其內容:

if __name__=="__main__":
    print "hello,i am run self"
else:
    print "hello,i am import by other"    

如果我們執行運行test1.py,如在命令行下執行: python test1.py,我們發現打印的是 hello,i am run self 。

如果我們在別的程序中導入test1.py,如 import test1。我們發現打印的是 hello,i am import by other。

__name__是一個系統變量,當其值是__main__時,表示它是作為主程序被執行的。

通過這種方式,我們就可以將一個py文件 作為主程序 和 模塊導入時 的差異化同時實現。

 


免責聲明!

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



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