第十篇:python的模塊與包

模塊的簡介

模塊就是一組功能的集合體,咱們的程序能夠導入模塊來複用模塊裏的功能。每個 Python 腳本.py文件均可以被當成是一個模塊。模塊以磁盤文件的形式存在。當一個模塊變得過大,而且驅動了太多功能的話,就應該考慮拆一些代碼出來另外建一個模塊。模塊裏的代碼能夠是一段直接執行的腳本,也能夠是一堆相似庫函數的代碼,從而能夠被別的模塊導 入(import)調用。模塊能夠包含直接運行的代碼塊、類定義、 函數定義或這幾者的組合。html

 

模塊分類:Python標準庫模塊、Python第三方模塊、應用程序自定義模塊。python

Python標準庫模塊:有些模塊直接被構建在解析器裏,這些雖然不是一些語言內置的功能,可是他卻能很高效的使用,甚至是系統級調用也沒問題。這些組件會根據不一樣的操做系統進行不一樣形式的配置,好比 winreg (Windows註冊表訪問)這個模塊就只會提供給 Windows 系統。應該注意到這有一個特別的模塊 sys ,它內置在每個 Python 解析器中。可參考:http://www.javashuo.com/article/p-ncoptfrs-cm.html程序員

https://blog.csdn.net/sadfishsc/article/details/10390065app

Python第三方模塊:非在解析器裏的他人寫的python腳本,可到官方提供的第三方模塊網址查找、下載:https://pypi.org/dom

應用程序自定義模塊:本身寫的python腳本。ide

模塊做用:函數

  1.可把程序分紅一個個的文件,這樣作程序的結構更清晰,方便管理。測試

   2.文件裏實現的功能可重複利用,提升開發效率。編碼

import 語句

在Python中用關鍵字import來引入某個模塊,好比要導入模塊time,就能夠在文件最開始或使用以前的地方用 import 來引入。spa

語法:import  模塊名

在調用模塊中的函數時,必須加上模塊名調用,由於可能存在多個模塊中含有相同名稱的函數,此時,若是隻是經過函數名來調用,解釋器沒法知道到底要調用哪一個函數。爲了不這樣的狀況,調用函數時,必須加上模塊名。

import加載的模塊分爲四個通用類別:

 

  1 使用python編寫的.py文件

 

  2 已被編譯爲共享庫或DLL的C或C++擴展

 

  3 包好一組模塊的包,把一系列模塊組織到一塊兒的文件夾(注:文件夾下有一個__init__.py文件,該文件夾稱之爲包)

 

  4 使用C編寫並連接到python解釋器的內置模塊

 

 

import time
time.sleep(1) #時間睡眠
# sleep()  #會報錯

咱們能夠從sys.module中找到當前已經加載的模塊,sys.module是一個字典,內部包含模塊名與模塊對象的映射,該字典決定了導入模塊時是否須要從新導入。

一行導入多個模塊

import time,random #模塊之間用逗號隔開
print(time.time())
print(random.randint(1,3))

導入模塊重命名:import ... as

 有時候你導入的模塊名稱已經在你的程序中使用了, 或者你不想使用現有的名稱。可使用一個新的名稱替換原始的名稱。

import time as tm
print(tm.time())

python模塊搜索路徑

在任何一個python程序啓動時,都會將模塊的搜索路徑收集到sys模塊的path屬性中(sys.path)。當python須要搜索模塊文件在何處時,首先搜索python解釋器的內置模塊(如time模塊),若是不是內置模塊,則搜索sys.path中的路徑列表,搜索時會從該屬性列出的路徑中按照從前向後的順序進行搜索,而且只要找到就當即中止搜索該模塊文件。因此若是當前路徑或 PythonPATH中存在與標準module一樣的module,則會覆蓋標準module。也就是說,若是當前目錄下存在xml.py,那麼在執行import xml時,導入的是當前目錄下的module,而不是系統標準的xml.py。

import sys
print(sys.path)

from ... import 語句

import語句是導入模塊中的全部屬性,而且訪問時須要使用模塊變量來引用,Python的from語句讓你從模塊中導入一個指定的部分到當前命名空間中。使用這種方式導入,不會整個模塊導入到當前命名空間,它只會將import的內容導入。from.....import 也支持as模式,給導入的模塊重命名。

from time import time,sleep
start_time = time()
print(start_time)
sleep(2)
stop_time = time()
print(stop_time)
print(stop_time-start_time)

導入一個模塊的全部內容也可使用from…import*,但不推薦使用,由於可能形成變量命名衝突。

from time import *
print(time()) #可直接使用time模塊的全部功能,不需在前綴加模塊名

命名衝突示例

from time import *
#假如咱們不知道time模塊裏是否有time(),但在導入time模塊的下方本身定義了一個名爲time的函數
def time():
    return '本身定義的time函數被調用'

#在本身定義的time函數下方使用from time import *
# from time import *
print(time()) #函數名即變量名儲存的是函數對象的引用,同名函數會存在引用覆蓋。

注意:當Python程序導入其餘模塊時,要避免循環導入,,即A文件導入了B,B文件又導入了A.,否則總會出意向不到的問題….

包(Package)

使用模塊能夠避免函數名和變量名衝突。相同名字的函數和變量徹底能夠分別存在不一樣的模塊中,所以,咱們本身在編寫模塊時,沒必要考慮名字會與其餘模塊衝突。可是也要注意,儘可能不要與內置函數名字衝突。爲了不模塊名衝突,Python又引入了按目錄來組織模塊的方法,稱爲包(Package)

 包是一種管理 Python 模塊命名空間的形式,採用"點模塊名稱"。好比一個模塊的名稱是 A.B, 那麼他表示一個包 A中的子模塊B

目錄中只有包含一個叫作__init__.py的文件纔會被認做是一個包,__init__.py文件是必須存在的,不然,Python就把這個目錄當成普通目錄,而不是一個包。__init__.py能夠是空文件,也能夠有Python代碼。當包看成模塊被導入時,__init__.py就會被執行加載。

在導入包的時候,Python會從sys.path中的目錄來尋找這個包中包含的子目錄。

Notes: 本身建立模塊時要注意命名,不能和Python自帶的模塊名稱衝突。例如,系統自帶了sys模塊,本身的模塊就不可命名爲sys.py,不然將沒法導入系統自帶的sys模塊。

類的屬性:
__doc__   文檔字符串
__name__   類名稱  
__bases__  父類組成的元組  
__dict__    保存類方法和變量等的字典    
實例的屬性:
__class__    實例所屬的類  
__dict__   保存實例數據的字典    
模塊的屬性:  
__dict__   與模塊相關的字典
__name__ 模塊的名稱
__file__   模塊文件的絕對路徑

 

導入包:

導入包與導入模塊的方式是同樣的。當執行導入包時,__init__.py就會被執行加載。因此包其實也是個模塊,__init__.py能夠是空文件,不過會影響from <包名> import * 和 import <包名> 這兩種導入方式。

建立__init__.py文件:

 目錄中只有包含了叫作__init__.py的文件,才能被程序認做是包,模塊才能被導入成功。如今咱們就在msg文件夾下建立一個__init__.py文件,而且在文件中寫入__all__=[ 'module_name', 'module_name' ]。

能夠在__init__.py中編寫其餘內容,在導入時,這些編寫的內容就會被執行。

能夠在__init__.py中向sys.path添加當前被調用模塊路徑。

__all__總結:

 編寫Python代碼(不建議在__init__中寫python模塊,能夠在包中在建立另外的模塊來寫,儘可能保證__init__.py簡單)。

模塊中不使用__all__屬性,則導入模塊內的全部公有屬性,方法和類 。 模塊中使用__all__屬性,包中使用__all__屬性,則表示只導入__all__中指定的屬性,定義了當咱們使用 from <module> import * 導入某個模塊/包的時候能導出的符號(這裏表明變量,函數,類等) (固然下劃線開頭的變量,方法和類除外)。

須要注意的是 __all__ 隻影響到了 from <package> import * 或 import <package> 這種導入方式, 對於 from <module|package> import <member> 導入方式並無影響,仍然能夠從外部導入。

reload()簡介:

 不管時import仍是from,默認狀況下,模塊在第一次被導入以後,其餘的導入都再也不有效。若是此時在另外一個窗口中改變並保存了模塊的源代碼文件,也沒法更新該模塊。這樣設計緣由在於,導入是一個開銷很大的操做(導入必須找到文件,將其編譯成字節碼,而且運行代碼),以致於每一個文件、每一個程序運行不可以重複多於一次。當一個模塊被導入到一個腳本,模塊代碼只會被執行一次。所以,若是你想從新執行模塊裏代碼,能夠用reload()函數,該函數會從新導入以前導入過的模塊。reload()是imp模塊中的一個函數,因此要使用imp.reload()以前,必須先導入imp,它的參數是一個已經成功被導入過的模塊變量。也就是說該模塊必須在內存中已經有本身的模塊對象。reload()會從新執行模塊文件,並將執行獲得的屬性徹底覆蓋到原有的模塊對象中。也就是說,reload()會從新執行模塊文件,但不會在內存中創建新的模塊對象,因此原有模塊對象中的屬性可能會被修改。語法:reload(module_name)

 

import random
def mul(x,y):
    return x*y
print(random.randint(3,3))  #此時random.randint()是隨機函數。
random.randint = mul  #變爲乘法函數
print(random.randint(3,3))
from imp import reload
reload(random)
print(random.randint(3,3)) #變回隨機函數。

 

導入模塊的細節

py文件分兩種:用於執行的程序文件和用於導入的模塊文件。當直接使用python a.py的時候表示a.py是用於執行的程序文件,經過import/from方式導入的py文件是模塊文件。

__name__屬性用來區分py文件是程序文件仍是模塊文件:

  • 當文件是程序文件的時候,該屬性被設置爲__main__
  • 當文件是模塊文件的時候(也就是被導入時),該屬性被設置爲自身模塊名

對於python來講,由於隱式自動設置,該屬性就有了特殊妙用:直接在模塊文件中經過if __name__ == "__main__"來判斷,而後寫屬於執行程序的代碼,若是直接用python執行這個文件,說明這個文件是程序文件,因而會執行屬於if代碼塊的代碼,若是是被導入,則是模塊文件,if代碼塊中的代碼不會被執行。

導入模塊的過程:

python的import是在程序運行期間執行的,並不是像其它不少語言同樣是在編譯期間執行。也就是說,import能夠出如今任何地方,只有執行到這個import行時,纔會執行導入操做。且在import某個模塊以前,沒法訪問這個模塊的屬性。

python在import導入模塊時,首先搜索模塊的路徑,而後編譯並執行這個模塊文件。雖然歸納起來只有兩個過程,但實際上很複雜。

Python中全部加載到內存的模塊都放在sys.modules。sys.modules是一個全局字典,該字典是python啓動後就加載在內存中。每當程序員import一個模塊文件時,首先會在這個字典查找是否已經加載了此模塊,若是加載了則只是將模塊的名字加入到正在調用import的模塊的Local名字空間中(若是在全局做用域導入模塊,則把模塊名加入全局命名稱空間,若是是在局部做用域導入模塊,則模塊名加入局部名稱空間)。若是沒有加載則從sys.path目錄中按照模塊名稱查找模塊文件,模塊文件能夠是py、pyc、pyd,找到後建立新的module對象(此時module對象的 __dict__ 屬性爲空),並將模塊名與module對象的引用加入字典sys.modules中,而後編譯、執行模塊文件載入內存,並填充module對象的屬性__dict__,按照必定的規則將一些結果放進這個module對象中,最後將模塊名稱導入到當前的Local名字空間。

注意細節:編譯、執行模塊文件、將結果保存到module對象中。

關於編譯、執行模塊文件的細說:

模塊第一次被導入的時候,會進行編譯,並生成.pyc字節碼文件,而後在python工做目錄的環境下執行這個pyc文件,而非模塊所在的目錄環境。當模塊被再次導入時,若是檢查到pyc文件的存在,且和源代碼文件的上一次修改時間戳mtime徹底對應(也就是說,編譯後模塊文件的源代碼沒有進行過修改),則直接裝載這個pyc文件並執行,不會再進行額外的編譯過程。固然,若是修改過模塊文件的源代碼,將會從新編譯獲得新的pyc文件。

注意,並不是全部的py文件都會生成編譯獲得的pyc文件,對於那些只執行一次的程序文件,會將內存中的編譯結果在執行完成後直接丟棄(多數時候如此,但仍有例外,好比使用compileall模塊能夠強制編譯成pyc文件),但模塊會將內存中的編譯結果持久化到pyc文件中。另外,運行字節碼pyc文件並不會比直接運行py文件更快,執行它也同樣是一行行地解釋、執行,惟一快的地方在於導入裝載的時候無需從新編譯而已。

執行模塊文件(已完成編譯)的時候,按照通常的執行流程執行:一行一行地、以代碼塊爲單元執行。通常地,模塊文件中只用來聲明變量、函數等屬性,以便提供給導入它的模塊使用,而不該該有其餘任何操做性的行爲,好比print()操做不該該出如今模塊文件中,但這並不是強制。

總之,執行完模塊文件後,這個模塊文件將有一個本身的全局名稱空間,在此模塊文件中定義的變量、函數等屬性,都會記錄在此名稱空間中。

最後,模塊的這些屬性都會保存到模塊對象中。因爲這個模塊對象賦值給了模塊變量module_name,因此經過變量module_name能夠訪問到這個對象中的屬性(好比變量、函數等),也就是模塊文件內定義的全局屬性。

參考連接https://www.cnblogs.com/qq78292959/archive/2013/05/17/3083961.html

https://www.jianshu.com/p/178c26789011

 建立模塊與包

目錄結構package目錄與test目錄是同級的:

 

print('package/package2目錄下的__init__模塊被加載')
package/package2/__init__.py
print('package/package2目錄下的module1.py執行加載')
print('package/package2目錄下module1模塊的__name__屬性值爲:{}'.format(__name__))
package/package2/module1.py
'''導入與父目錄的父目錄同級的模塊'''
# import test.module1 as md
from test import module1
package/package2/test1.py
__all__ = ['module1']
print('package目錄下的__init__模塊被加載')
package/__init__.py
print('package目錄下的module1.py執行加載')
print('package目錄下的module1模塊的__name__屬性值爲:{}'.format(__name__))
def add(x,y):
    return x+y

def mul(x,y):
    return x*y
package/module1.py
'''同目錄下的模塊導入方式'''
# import module1
# from package import module1
# from module2 import *  #不推薦使用,避免命名衝突
# print('package目錄下的module1.py執行加載')
# print('module1模塊的__name__屬性值爲:{}'.format(__name__))
# def add(x,y,z):
#     return x+y+z
# def mul(x,y,z):
#     return x*y*z
# print(module1.add(2,3))

'''導入與父目錄同級的模塊的方法'''
# import test #導入test包,實質上是導入test目錄下的 __init__模塊,此模塊有什麼屬性,導入的test就有什麼屬性。
# print(test.module1.__name__)
# import test.module1 as md
# from test import module1

'''導入同目錄下的包'''
# import package2.module1 as md
# from package2 import module1
package/test1.py
print('test目錄下的__init__.py被加載')
__all__ = ['module1'] # 若容許經過包名導入全部模塊,必須給__all__實現賦值
from test import * #表示導入同目錄的全部模塊
test/__init__.py
print('test目錄下的module1被加載')
test/module1.py

 若python解釋器沒法找到可根據模塊的所在目錄給sys.path添加搜索路徑:

 

 或是:

import sys,os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_DIR)
from bao1 import a1
a1.aa1(99)

編寫標準模塊的模板

編寫一個hello.py的模塊

 1 #!/usr/bin/env python3 
 2 # -*- coding: utf-8 -*-
 3 
 4 ' a test module '
 5 
 6 __author__ = 'Michael Liao'
 7 
 8 import sys
 9 
10 def test():
11     args = sys.argv   # argv參數用列表存儲命令行的全部參數
12     if len(args)==1:  # 當列表長度爲1時即只有一個參數時
13         print('Hello, world!')
14     elif len(args)==2: # 當命令行有兩個參數時
15         print('Hello, %s!' % args[1])
16     else:
17         print('Too many arguments!')
18 
19 if __name__=='__main__':
20     test()

第1行和第2行是標準註釋,第1行註釋可讓這個hello.py文件直接在Unix/Linux/Mac上運行,第2行註釋表示.py文件自己使用標準UTF-8編碼;

第4行是一個字符串,表示模塊的文檔註釋,任何模塊代碼的第一個字符串都被視爲模塊的文檔註釋;

第6行使用__author__變量把做者寫進去,這樣當你公開源代碼後別人就能夠瞻仰你的大名;

第8行是導入sys模塊,導入sys模塊後,咱們就有了變量sys指向該模塊,利用sys這個變量,就能夠訪問sys模塊的全部功能。sys模塊有一個argv變量,用list存儲了命令行的全部參數。argv至少有一個元素,由於第一個參數永遠是該.py文件的名稱,例如:運行python3 hello.py得到的sys.argv就是['hello.py'],注意這裏python3不算是參數;運行python3 hello.py Michael得到的sys.argv就是['hello.py', 'Michael]

第19行表示當咱們在命令行運行hello模塊文件時,Python解釋器把一個特殊變量__name__置爲__main__,而若是在其餘地方導入該hello模塊時,if判斷將失敗,所以,這種if測試可讓一個模塊經過命令行運行時執行一些額外的代碼,最多見的就是運行測試。

以上就是Python模塊的標準文件模板,固然也能夠所有刪掉不寫,可是,按標準辦事確定沒錯。

相關文章
相關標籤/搜索