目錄
本文將在學(xué)習(xí)Python中模塊的概念的基礎(chǔ)上,通過一些示例來繼續(xù)學(xué)習(xí)模塊標(biāo)準(zhǔn)模板、import、from…import 、深入理解模塊、__name__屬性、包等知識。
模塊標(biāo)準(zhǔn)模板
在了解了Python中的模塊知識以后,就來看一下Python中的模塊標(biāo)準(zhǔn)模板。例子中模塊名:17my_module.py。
如下:
# encoding: utf-8
#! F:\python_projects\17my_module.py
# 文件名: 17my_module.py
"""
Here is document note
這里是文檔注釋
"""
__author__ = 'Guang TouQiang'
import sys
print('命令行參數(shù)如下:')
for i in sys.argv:
print(i)
print('\n\nPython 路徑為:', sys.path, '\n')
解釋如下:
第1、第2和第3行是標(biāo)準(zhǔn)注釋。其中:
第1行注釋表示當(dāng)前模塊采用的是標(biāo)準(zhǔn)utf-8編碼。
第2行注釋表示可以讓這個17my_module.py文件直接在Unix/Linux/Mac上運(yùn)行。
第3行注釋表示本模塊名;
第4-7行是一個字符串,表示模塊的文檔注釋,任何模塊代碼的第一個字符串都被視為模塊的文檔注釋;
第8行使用_author_變量把作者寫進(jìn)去,這樣當(dāng)你公開源代碼后別人就可以看到你的大名;
以上就是Python模塊的標(biāo)準(zhǔn)文件模板,當(dāng)然也可以全部刪掉不寫,但是,按標(biāo)準(zhǔn)辦事肯定沒錯。
后面開始就是真正的代碼部分。
你可能注意到了,使用sys模塊的第一步,就是導(dǎo)入該模塊:
import sys
導(dǎo)入sys模塊后,我們就有了變量sys指向該模塊,利用sys這個變量,就可以訪問sys模塊的所有功能。sys模塊有一個argv變量,用list存儲了命令行的所有參數(shù)。
import 語句
想使用 Python 源文件,只需在另一個源文件里執(zhí)行 import 語句,語法如下:
import module1[, module2[,... moduleN]
當(dāng)解釋器遇到 import 語句,如果模塊在當(dāng)前的搜索路徑就會被導(dǎo)入。
搜索路徑是一個解釋器會先進(jìn)行搜索的所有目錄的列表。例如:想要導(dǎo)入模塊my_pi,需要把命令放在腳本的頂端:
my_pi.py源碼如下:
# 割圓求π
# 獲取正2N邊形長度
def get_n(n):
# 正6邊形邊長等于圓的半徑,假設(shè)圓的半徑為1,則邊長也為1
if n == 6.0:
print("L6 = 1.0")
return 1.0
else:
a = n / 2
result = math.sqrt(2 - math.sqrt(4 - math.pow(get_n(a), 2)))
print(r'內(nèi)接正' + str(n).replace('.0', '') + r'邊形邊長 = ' + str(result))
return result
# 獲取π
def get_pi(n):
return n * get_n(n) / 2
在my_module模塊中導(dǎo)入,就可以使用了:
import my_pi
# 獲取π
print(r'π = ' + str(my_pi.get_pi(384*8*8)))
運(yùn)行結(jié)果如下:
L6 = 1.0
內(nèi)接正12邊形邊長 = 0.5176380902050416
內(nèi)接正24邊形邊長 = 0.2610523844401031
內(nèi)接正48邊形邊長 = 0.13080625846028635
內(nèi)接正96邊形邊長 = 0.0654381656435527
內(nèi)接正192邊形邊長 = 0.03272346325297234
內(nèi)接正384邊形邊長 = 0.01636227920787303
內(nèi)接正768邊形邊長 = 0.008181208052471188
內(nèi)接正1536邊形邊長 = 0.004090612582339534
內(nèi)接正3072邊形邊長 = 0.0020453073607051096
內(nèi)接正6144邊形邊長 = 0.00102265381399354
內(nèi)接正12288邊形邊長 = 0.0005113269236068993
內(nèi)接正24576邊形邊長 = 0.0002556634639747083
π = 3.1415926453212157
一個模塊只會被導(dǎo)入一次,不管你執(zhí)行了多少次import。這樣可以防止導(dǎo)入模塊被一遍又一遍地執(zhí)行。
當(dāng)我們使用import語句的時候,Python解釋器是怎樣找到對應(yīng)的文件的呢?
這就涉及到Python的搜索路徑,搜索路徑是由一系列目錄名組成的,Python解釋器就依次從這些目錄中去尋找所引入的模塊。這看起來很像環(huán)境變量,事實(shí)上,也可以通過定義環(huán)境變量的方式來確定搜索路徑。搜索路徑是在Python編譯或安裝的時候確定的,安裝新的庫應(yīng)該也會修改。
from…import 語句
Python的from語句讓你從模塊中導(dǎo)入一個指定的部分到當(dāng)前命名空間中,語法如下:
from modname import name1[, name2[, ... nameN]]
例如:my_pi.py模塊中,添加了兩個函數(shù),get_standard_pi、get_simple_pi源碼如下:
# 割圓求π
# 獲取正2N邊形長度
def get_n(n):
# 正6邊形邊長等于圓的半徑,假設(shè)圓的半徑為1,則邊長也為1
if n == 6.0:
print("L6 = 1.0")
return 1.0
else:
a = n / 2
result = math.sqrt(2 - math.sqrt(4 - math.pow(get_n(a), 2)))
print(r'內(nèi)接正' + str(n).replace('.0', '') + r'邊形邊長 = ' + str(result))
return result
# 獲取標(biāo)準(zhǔn)pi
def get_standard_pi():
return 3.1415926
# 獲取簡單Pi
def get_simple_pi():
return 3.14
# 利用割圓方法獲取π
def get_pi(n):
return n * get_n(n) / 2
錯誤示例
在模塊my_module中,這次只導(dǎo)入get_standard_pi函數(shù)。導(dǎo)入get_standard_pi,仍調(diào)用my_pi.get_pi(),則會報錯NameError,代碼如下:
from my_pi import get_standard_pi
# 獲取π
print(r'π = ' + str(my_pi.get_pi(384*8*8)))
運(yùn)行結(jié)果:
Traceback (most recent call last):
File "F:/python_projects/17my_module.py", line 13, in <module>
print(r'π = ' + str(my_pi.get_pi(384*8*8)))
NameError: name 'my_pi' is not defined
原因是:由于from my_pi import get_standard_pi,這個聲明不會把整個my_pi模塊導(dǎo)入到當(dāng)前的命名空間中,它只會將my_pi里的get_standard_pi函數(shù)引入進(jìn)來。而實(shí)際調(diào)用時,調(diào)用的get_pi(),所以提示:NameError: name 'my_pi' is not defined,my_pi未定義。
正確示例
在模塊my_module中,這次只導(dǎo)入get_standard_pi()函數(shù)。導(dǎo)入get_standard_pi,仍調(diào)用my_pi.get_standard_pi(),代碼如下:
from my_pi import get_standard_pi
# 獲取π
print(r'π = ' + str(get_standard_pi()))
運(yùn)行結(jié)果:
π = 3.1415926
From…import* 語句
把一個模塊的所有內(nèi)容全都導(dǎo)入到當(dāng)前的命名空間也是可行的,只需使用如下聲明:
from module_name import *
這提供了一個簡單的方法來導(dǎo)入一個模塊中的所有項(xiàng)目。然而這種聲明不該被過多地使用,此處也不再舉例。
深入理解模塊
每個模塊有各自獨(dú)立的符號表,在模塊內(nèi)部為所有的函數(shù)當(dāng)作全局符號表來使用。所以,模塊的作者可以放心大膽的在模塊內(nèi)部使用這些全局變量,而不用擔(dān)心把其他用戶的全局變量搞污染。
在一個模塊(或者腳本,或者其他地方)的最前面使用 import 來導(dǎo)入一個模塊,當(dāng)然這只是一個慣例,而不是強(qiáng)制的。被導(dǎo)入的模塊的名稱將被放入當(dāng)前操作的模塊的符號表中。
例如:上面例子中的
import my_pi
這樣做并沒有把直接定義在my_pi中的函數(shù)名稱寫入到當(dāng)前符號表里,只是把模塊my_pi的名字寫到了那里。
還有一種導(dǎo)入的方法,可以使用 import 直接把模塊內(nèi)(函數(shù),變量的)名稱導(dǎo)入到當(dāng)前操作模塊。比如:
from my_pi import get_standard_pi
這種導(dǎo)入的方法不會把被導(dǎo)入的模塊的名稱放在當(dāng)前的字符表中(所以在這個例子里面,my_pi這個名稱是沒有定義的)。
這還有一種方法,可以一次性的把模塊中的所有(函數(shù),變量)名稱都導(dǎo)入到當(dāng)前模塊的字符表:
from module_name import *
這將把所有的名字都導(dǎo)入進(jìn)來,但是那些由單一下劃線_開頭的名字不在此例。大多數(shù)情況, Python程序員不使用這種方法,因?yàn)橐氲钠渌鼇碓吹拿?,很可能覆蓋了已有的定義。
這也是三種導(dǎo)入方式的重要區(qū)別。
__name__屬性
一個模塊被另一個程序第一次引入時,其主程序?qū)⑦\(yùn)行。如果我們想在模塊被引入時,模塊中的某一程序塊不執(zhí)行,我們可以用__name__屬性來使該程序塊僅在該模塊自身運(yùn)行時執(zhí)行。
基于此,設(shè)計如下實(shí)例進(jìn)行驗(yàn)證:
我們創(chuàng)建2個模塊:my_name.py、18name.py,模塊名分別:my_name、18name;在18name模塊中導(dǎo)入my_name模塊。
其中my_name.py源碼如下:
# 定義一個測試函數(shù)
def name_test():
if __name__ == '__main__':
print('A: __name__ is __main__')
else:
print('B: __name__ is ', __name__)
18name.py源碼如下:
# 導(dǎo)入my_name模塊
import my_name
# 調(diào)用my_name模塊中的name_test函數(shù)
my_name.name_test()
運(yùn)行模塊18name,運(yùn)行結(jié)果如下:
B: __name__ is my_name
修改:my_name.py源碼如下:
# 定義一個測試函數(shù)
def name_test():
if __name__ == '__main__':
print('A: __name__ is __main__')
else:
print('B: __name__ is ', __name__)
# 在本模塊中調(diào)用定義好的測試函數(shù)
name_test()
運(yùn)行模塊my_name,運(yùn)行結(jié)果如下:
A: __name__ is __main__
說明: 每個模塊都有一個__name__屬性,當(dāng)其值是'__main__'時,表明該模塊自身在運(yùn)行,否則是被引入。
提示:__name__ 與 __main__ 底下是雙下劃線, _ _ 是這樣去掉中間的那個空格。
包
通過前面的學(xué)習(xí),我們已經(jīng)知道,在解決和其他人的模塊同名時,我們引入了包。此時,模塊的名字就變成了:包名.模塊名。
注意:
目錄只有包含一個叫做__init__.py的文件才會被認(rèn)作是一個包,主要是為了避免一些濫俗的名字(比如叫做 string)不小心的影響搜索路徑中的有效模塊。
最簡單的情況,放一個空的 __init__.py就可以了。當(dāng)然這個文件中也可以包含一些初始化代碼或者為(將在后面介紹的) __all__變量賦值。
注意當(dāng)使用from package import item這種形式的時候,對應(yīng)的item既可以是包里面的子模塊(子包),或者包里面定義的其他名稱,比如函數(shù),類或者變量。
import語法會首先把item當(dāng)作一個包定義的名稱,如果沒找到,再試圖按照一個模塊去導(dǎo)入。如果還沒找到,恭喜,一個ImportError異常被拋出了。
反之,如果使用形如import item.subitem.subsubitem這種導(dǎo)入形式,除了最后一項(xiàng),都必須是包,而最后一項(xiàng)則可以是模塊或者是包,但是不可以是類,函數(shù)或者變量的名字。
從一個包中導(dǎo)入*
使用形如from package.xxx import *時,Python會做哪些事情呢?
Python 會進(jìn)入文件系統(tǒng),找到這個包里面所有的子模塊,一個一個的把它們都導(dǎo)入進(jìn)來。
但是很不幸,這個方法在 Windows平臺上工作的就不是非常好,因?yàn)閃indows是一個大小寫不區(qū)分的系統(tǒng)。
在這類平臺上,沒有人敢擔(dān)保一個叫做 ECHO.py 的文件導(dǎo)入為模塊 echo 還是 Echo 甚至 ECHO。
(例如,Windows 95就很討厭的把每一個文件的首字母大寫顯示)而且 DOS 的 8+3 命名規(guī)則對長模塊名稱的處理會把問題搞得更糾結(jié)。
為了解決這個問題,只能煩勞包的作者提供一個精確的包的索引了。
導(dǎo)入語句遵循如下規(guī)則:
如果包定義文件__init__.py存在一個叫做__all__的列表變量,那么在使用from package import *的時候就把這個列表中的所有名字作為包內(nèi)容導(dǎo)入。
作為包的作者,可別忘了在更新包之后保證__all__也更新了啊。你說我就不這么做,我就不使用導(dǎo)入*這種用法,好吧,沒問題,誰讓你是老板呢。
例如:我們項(xiàng)目中有一個登陸包,包名:login。

如上圖,在
company_project/login/__init__.py中包含如下代碼:__all__ = ["abc", "cde", "xyz"]這表示當(dāng)你使用
from company_project.login import *這種用法時,你只會導(dǎo)入包里面這三個子模塊。
如果 __all__ 真的沒有定義,那么使用from company_project.login import *這種語法的時候,就不會導(dǎo)入包 login 里的任何子模塊。他只是把包c(diǎn)ompany_project.login和它里面定義的所有內(nèi)容導(dǎo)入進(jìn)來(可能運(yùn)行__init__.py里定義的初始化代碼)。這會把 __init__.py 里面定義的所有名字導(dǎo)入進(jìn)來。并且他不會破壞掉我們在這句話之前導(dǎo)入的所有明確指定的模塊。
通常我們并不主張使用*這種方法來導(dǎo)入模塊,因?yàn)檫@種方法經(jīng)常會導(dǎo)致代碼的可讀性降低。不過這樣倒的確是可以省去不少敲鍵的功夫,而且一些模塊都設(shè)計成了只能通過特定的方法導(dǎo)入。
記住,使用from Package import specific_submodule這種方法永遠(yuǎn)不會有錯。事實(shí)上,這也是推薦的方法。除非是你要導(dǎo)入的子模塊有可能和其他包的子模塊重名。
如果在結(jié)構(gòu)中login包是一個子包(比如這個例子中對于包company_project來說),而你又想導(dǎo)入兄弟包(同級別的包,圖中沒有,假設(shè)有個網(wǎng)絡(luò)請求的network包)你就得使用導(dǎo)入絕對的路徑來導(dǎo)入。比如,如果模塊company_project.login 要使用包company_project.network中的模塊query,你就要寫成 from company_project.network import query。
無論是隱式的還是顯式的相對導(dǎo)入都是從當(dāng)前模塊開始的。主模塊的名字永遠(yuǎn)是"__main__",一個Python應(yīng)用程序的主模塊,應(yīng)當(dāng)總是使用絕對路徑引用。
包還提供一個額外的屬性__path__。這是一個目錄列表,里面每一個包含的目錄都有為這個包服務(wù)的__init__.py,你得在其他__init__.py被執(zhí)行前定義哦。可以修改這個變量,用來影響包含在包里面的模塊和子包。
說明: 這個功能并不常用,一般用來擴(kuò)展包里面的模塊。
小結(jié)
本文在學(xué)習(xí)Python3中模塊概念的基礎(chǔ)上,通過一些示例來繼續(xù)學(xué)習(xí)了模塊標(biāo)準(zhǔn)模板、import、from…import 、深入理解模塊、__name__屬性、包等知識。
當(dāng)前所在位置(局部視圖)

當(dāng)前所在位置(全局視圖)
