Python入門第6/10頁(yè)
更新時(shí)間:2007年02月08日 00:00:00 作者:
第六章 模塊
如果退出Python解釋程序然后再進(jìn)入,原有的定義(函數(shù)和變量)就丟失了。所以,如果需要寫長(zhǎng)一點(diǎn)的程序,最好用一個(gè)文本編輯程序?yàn)榻忉尦绦驕?zhǔn)備輸入,然后以程序文件作為輸入來(lái)運(yùn)行Python解釋程序,這稱為準(zhǔn)備腳本(script)。當(dāng)你的程序變長(zhǎng)時(shí),最好把它拆分成幾個(gè)文件以利于維護(hù)。你還可能想在幾個(gè)程序中都使用某個(gè)很方便的函數(shù),但又不想把函數(shù)定義賦值到每一個(gè)程序中。
為了支持這些,Python有一種辦法可以把定義放在一個(gè)文件中然后就可以在一個(gè)腳本中或交互運(yùn)行中調(diào)用。這樣的文件叫做一個(gè)模塊;模塊中的定義可以導(dǎo)入其它模塊或主模塊(主模塊指在解釋程序頂級(jí)執(zhí)行的腳本或交互執(zhí)行的程序所能訪問(wèn)的變量集合)。
模塊是包含了Python定義和語(yǔ)句的文件。文件名由模塊名加上后綴“.py”構(gòu)成。在模塊內(nèi),模塊的名字(作為一個(gè)字符串)可以由全局變量__name__的值獲知。例如,在Python的搜索路徑中用你習(xí)慣使用的文本編輯器(Python 1.5.2包含了一個(gè)用Tkinter編寫的IDLE集成開發(fā)環(huán)境,MS Windows下有一個(gè)PythonWin界面也可以進(jìn)行Python程序編輯)生成一個(gè)名為“fibo.py ”的文件,包含如下內(nèi)容:
# Fibonacci numbers module
def fib(n): # 輸出小于n的Fibonacci序列
a, b = 0, 1
while b < n:
print b,
a, b = b, a+b
def fib2(n): # 返回小于n的Fibonacci序列
result = []
a, b = 0, 1
while b < n:
result.append(b)
a, b = b, a+b
return result
然后進(jìn)入Python解釋程序(在IDLE或PythonWin中可以直接進(jìn)入解釋程序窗口),用如下命令可以導(dǎo)入模塊:
>>> import fibo
這不會(huì)把模塊fibo中的函數(shù)的名字直接引入當(dāng)前的符號(hào)表,這只是把模塊名fibo引入??梢杂媚K名來(lái)訪問(wèn)其中的函數(shù):
>>> fibo.fib(1000)
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
>>> fibo.fib2(100)
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
>>> fibo.__name__
'fibo'
如果經(jīng)常使用某個(gè)函數(shù)可以給它賦一個(gè)局部名字:
>>> fib = fibo.fib
>>> fib(500)
1 1 2 3 5 8 13 21 34 55 89 144 233 377
6.1 模塊的進(jìn)一步介紹
模塊除了可以包含函數(shù)定義之外也可以包含可執(zhí)行語(yǔ)句。這些可執(zhí)行語(yǔ)句用來(lái)初始化模塊,它們只在模塊第一次被導(dǎo)入時(shí)執(zhí)行。
每個(gè)模塊有自己私有的符號(hào)表,這個(gè)私有符號(hào)表對(duì)于模塊中的所有函數(shù)而言卻是它們的全局符號(hào)表。因此,模塊作者可以在模塊中使用全局變量而不需擔(dān)心與模塊用戶的全局變量沖突。另一方面,如果你有把握的話也可以用訪問(wèn)模塊中函數(shù)的格式,即modname.itemname的方法來(lái)修改模塊中的全局變量。
模塊可以導(dǎo)入其它模塊。我們通常把所有的導(dǎo)入語(yǔ)句放在模塊(或腳本)的開始位置,這不是規(guī)定要求的。導(dǎo)入的模塊名放入模塊的全局符號(hào)表中。
導(dǎo)入還有另一種用法,可以把模塊中的名字直接導(dǎo)入使用者的符號(hào)表。例如:
>>> from fibo import fib, fib2
>>> fib(500)
1 1 2 3 5 8 13 21 34 55 89 144 233 377
這不會(huì)把模塊名導(dǎo)入使用者的符號(hào)表中(例如,上面例子中fibo就沒(méi)有定義)。
還有一種辦法可以導(dǎo)入一個(gè)模塊中定義的所有名字:
>>> from fibo import *
>>> fib(500)
1 1 2 3 5 8 13 21 34 55 89 144 233 377
這可以把模塊中除了以下劃線結(jié)尾的所有名字導(dǎo)入。
6.1.1 模塊搜索路徑
在導(dǎo)入名為spam的模塊時(shí),解釋程序先在當(dāng)前目錄中尋找名為“spam.py”的文件,然后從環(huán)境變量PYTHONPATH所定義的目錄列表中尋找。PYTHONPATH的用法和可執(zhí)行文件的搜索路徑PATH用法相同,都是一個(gè)目錄列表。當(dāng)PYTHONPATH未設(shè)置的時(shí)候,或者文件仍找不到,則搜索繼續(xù)在安裝時(shí)設(shè)定的缺省路徑搜索,在Unix中,這通常是“.:/usr/local/lib/python” 。
實(shí)際上,模塊是按變量sys.path指定的路徑搜索的,此變量在解釋程序啟動(dòng)時(shí)初始化為包含輸入腳本的目錄(或當(dāng)前路徑),PYTHONPATH和安裝缺省路徑。這樣,用戶可以通過(guò)修改sys.path 來(lái)修改和替換模塊搜索路徑。參見(jiàn)后面關(guān)于標(biāo)準(zhǔn)模塊的一節(jié)。
6.1.2 “編譯”的Python文件
為了提高調(diào)用許多標(biāo)準(zhǔn)模塊的小程序的啟動(dòng)時(shí)間,一個(gè)重要的措施是,如果在找到“spam.py ”的目錄中存在一個(gè)名為“spam.pyc”的文件,就認(rèn)為此文件包含了模塊spam的一個(gè)所謂“ 字節(jié)編譯”版本。用于生成“spam.pyc”的“spam.py”的修改時(shí)間被記入了“spam.pyc”中,如果記錄的修改時(shí)間與現(xiàn)在文件的時(shí)間不相符的話就忽略編譯文件。
一般不需要自己生成“spam.pyc”這樣的編譯文件。每當(dāng)“spam.py”成功編譯后解釋程序就嘗試寫編譯版本“spam.pyc”,如果不可寫也不會(huì)出錯(cuò);如果因?yàn)槟撤N原因此文件沒(méi)有寫完則生成的“spam.pyc”被識(shí)別為不完整的而被忽略。編譯文件“spam.pyc”的格式是不依賴于平臺(tái)的,所以不同結(jié)構(gòu)的機(jī)器可以共享Python模塊目錄。
下面是對(duì)專家的一些竅門:
如果Python解釋程序是以-O標(biāo)志啟動(dòng)的,將生成優(yōu)化的編譯代碼,保存在“.pyo”文件中。目前優(yōu)化不是很多,現(xiàn)在只是去掉assert語(yǔ)句和SET_LINENO指令。使用了-O標(biāo)志時(shí),所有字節(jié)碼都是優(yōu)化的,“.pyc”文件被忽略,“.py”文件被編譯為優(yōu)化的字節(jié)碼。
給Python解釋程序兩個(gè)優(yōu)化標(biāo)志(-OO)產(chǎn)生的優(yōu)化代碼有時(shí)會(huì)導(dǎo)致程序運(yùn)行不正常。目前雙重優(yōu)化只從字節(jié)碼中刪除了__doc__字符串,使得“.pyo”文件較小。有些程序可能是依賴于文檔字符串的,所以只有在確知不會(huì)有問(wèn)題時(shí)才可以使用這樣的優(yōu)化。
從“.pyc”或“.pyo”讀入的程序并不能比從“.py”讀入的運(yùn)行更快,它們只是調(diào)入速度更快一些。
如果一個(gè)程序是用在命令行指定腳本文件名的方式運(yùn)行的,腳本的字節(jié)碼不會(huì)寫入“.pyc ”或“.pyo”文件。所以如果把程序的主要代碼都移入一個(gè)模塊,腳本中只剩下導(dǎo)入該模塊的引導(dǎo)程序則可以略微縮短腳本的啟動(dòng)時(shí)間。
可以有叫做“spam.pyc”(當(dāng)用了-O標(biāo)志時(shí)為“spam.pyo”)的文件而沒(méi)有對(duì)應(yīng)的源文件“spam.py”。這可以用來(lái)分發(fā)一個(gè)比較難反編譯的Python代碼庫(kù)。
模塊compileall可以把一個(gè)目錄中所有模塊編譯為“.pyc”文件(指定了-O選項(xiàng)時(shí)編譯為“.pyo”文件)。
6.2 標(biāo)準(zhǔn)模塊
Python帶有一個(gè)標(biāo)準(zhǔn)模塊庫(kù),在另一個(gè)文檔《Python庫(kù)參考》中進(jìn)行了描述。一些模塊直接編入了解釋程序中,這些模塊不是語(yǔ)言的核心,為了運(yùn)行效率或者為了提供對(duì)于系統(tǒng)調(diào)用這樣的系統(tǒng)底層功能而編入了解釋程序中。提供那些模塊是編譯時(shí)的選擇,例如,amoeba模塊只在提供amoeba底層指令的系統(tǒng)中才能提供。
有一個(gè)模塊值得特別重視:sys模塊,每一個(gè)Python解釋程序中都編譯入了這個(gè)模塊。變量sys.ps1和sys.ps2定義了交互運(yùn)行時(shí)的初始提示和續(xù)行提示。
>>> import sys
>>> sys.ps1
'>>> '
>>> sys.ps2
'... '
>>> sys.ps1 = 'C> '
C> print 'Yuck!'
Yuck!
C>
這兩個(gè)變量只在解釋程序以交互方式運(yùn)行時(shí)才有定義。
變量sys.path是一個(gè)字符串列表,由它確定解釋程序的模塊搜索路徑。它被初始化為環(huán)境變量PYTHONPATH所指定的缺省路徑,環(huán)境變量沒(méi)有定義時(shí)初始化為安裝時(shí)的缺省路徑??梢杂脴?biāo)準(zhǔn)的列表操作修改這個(gè)搜索路徑,例如:
>>> import sys
>>> sys.path.append('/ufs/guido/lib/python')
6.3 dir()函數(shù)
內(nèi)置函數(shù)dir()用于列出一個(gè)模塊所定義的名字,它返回一個(gè)字符串列表:
>>> import fibo, sys
>>> dir(fibo)
['__name__', 'fib', 'fib2']
>>> dir(sys)
['__name__', 'argv', 'builtin_module_names', 'copyright', 'exit',
'maxint', 'modules', 'path', 'ps1', 'ps2', 'setprofile', 'settrace',
'stderr', 'stdin', 'stdout', 'version']
沒(méi)有自變量時(shí),dir()列出當(dāng)前定義的名字。
>>> a = [1, 2, 3, 4, 5]
>>> import fibo, sys
>>> fib = fibo.fib
>>> dir()
['__name__', 'a', 'fib', 'fibo', 'sys']
注意dir()列出了所有各類名字:變量名、模塊名、函數(shù)名,等等。dir()不會(huì)列出內(nèi)置函數(shù)、變量的名字。要想列出內(nèi)置名字的話需要使用標(biāo)準(zhǔn)模塊__builtin__:
>>> import __builtin__
>>> dir(__builtin__)
['AccessError', 'AttributeError', 'ConflictError', 'EOFError', 'IOError',
'ImportError', 'IndexError', 'KeyError', 'KeyboardInterrupt',
'MemoryError', 'NameError', 'None', 'OverflowError', 'RuntimeError',
'SyntaxError', 'SystemError', 'SystemExit', 'TypeError', 'ValueError',
'ZeroDivisionError', '__name__', 'abs', 'apply', 'chr', 'cmp', 'coerce',
'compile', 'dir', 'divmod', 'eval', 'execfile', 'filter', 'float',
'getattr', 'hasattr', 'hash', 'hex', 'id', 'input', 'int', 'len', 'long',
'map', 'max', 'min', 'oct', 'open', 'ord', 'pow', 'range', 'raw_input',
'reduce', 'reload', 'repr', 'round', 'setattr', 'str', 'type', 'xrange']
6.4 包
Python中可以用“包”來(lái)組織Python的模塊名字空間,名字引用時(shí)可以用“帶點(diǎn)的模塊名。例如,模塊名A.B代表包“A”內(nèi)名為“B”的子模塊。正如使用模塊可以使不同模塊的作者不用顧慮彼此的全局變量名會(huì)沖突,使用帶點(diǎn)的模塊名可以使多模塊包如NumPy和PIL的作者不需要擔(dān)心彼此的模塊名會(huì)沖突。
假設(shè)你有一系列處理聲音文件和聲音數(shù)據(jù)的模塊(稱為一個(gè)“包”)。有許多種不同的聲音文件格式(通常用擴(kuò)展名來(lái)識(shí)別,如“wav”,“.aiff”,“.au”),所以你可能需要制作并維護(hù)一組不斷增加的模塊來(lái)處理不同文件格式的轉(zhuǎn)換。你還可能需要對(duì)聲音數(shù)據(jù)進(jìn)行許多不同的操作(如混音、回響、均衡、產(chǎn)生模擬立體聲效果),所以你還需要不斷增加模塊來(lái)執(zhí)行這些操作。一下是你的程序包的可能的結(jié)構(gòu)(用一個(gè)分層文件系統(tǒng)表示):
Sound/ 頂層包
__init__.py 初始化音響包
Formats/ 用于文件格式轉(zhuǎn)換的子程序包
__init__.py
wavread.py
wavwrite.py
aiffread.py
aiffwrite.py
auread.py
auwrite.py
...
Effects/ 用于音響效果的子程序包
__init__.py
echo.py
surround.py
reverse.py
...
Filters/ 用于濾波的子程序包
__init__.py
equalizer.py
vocoder.py
karaoke.py
...
包目錄中的“__init__.py”文件是必須得,用來(lái)指示Python把這個(gè)目錄看成包,這可以防止有相同名字如“string”的子目錄掩蓋住在搜索路徑后面一些出現(xiàn)的模塊定義。在最簡(jiǎn)單的情況下,“__init__.py”可以是一個(gè)空文件,它也可以包含初始化包所需的代碼,和設(shè)置“__all__”變量,這些后面會(huì)加以討論。
包的用戶可以從包中導(dǎo)入單獨(dú)的模塊,如:
import Sound.Effects.echo
這可以把子模塊Sound.Effects.echo導(dǎo)入。要引用它也必須用全名,例如:
Sound.Effects.echo.echofilter(input, output, delay=0.7, atten=4)
導(dǎo)入子模塊的另一種辦法是:
from Sound.Effects import echo
這同樣也導(dǎo)入子模塊echo,但調(diào)用時(shí)不需寫包前綴,所以可以用如:
echo.echofilter(input, output, delay=0.7, atten=4)
另外一種寫法是直接導(dǎo)入所需的函數(shù)或變量:
from Sound.Effects.echo import echofilter
這一次同樣是調(diào)入了子模塊echo,但是使其函數(shù)echofilter直接可用:
echofilter(input, output, delay=0.7, atten=4)
注意使用“from 包 import 項(xiàng)”這樣的格式時(shí),導(dǎo)入的項(xiàng)可以是包的一個(gè)子模塊(或子包),也可以是包內(nèi)定義的其它名字如函數(shù)、類、變量。導(dǎo)入語(yǔ)句首先查找包內(nèi)是否定義了所需的項(xiàng),如果沒(méi)有則假設(shè)它是一個(gè)模塊然后調(diào)入。如果找不到,結(jié)果引起ImportError。
相反的,當(dāng)使用“import item.subitem.subsubitem”這樣的格式時(shí),除最后一個(gè)外其它各項(xiàng)都應(yīng)該是包,最后一項(xiàng)可以是包也可以是模塊,不允許是前面一項(xiàng)內(nèi)部定義的類、函數(shù)或變量。
6.4.1 從包中導(dǎo)入*
現(xiàn)在,如果用戶寫“from Sound.Effects import *”會(huì)發(fā)生什么情況?理想情況下我們希望這應(yīng)該掃描文件系統(tǒng),找到所有包內(nèi)的子模塊并把它們都導(dǎo)入進(jìn)來(lái)。不幸的是這種操作在Mac和Windows平臺(tái)上不能準(zhǔn)確實(shí)現(xiàn),這兩種操作系統(tǒng)對(duì)文件名的大小寫沒(méi)有準(zhǔn)確信息。在這些平臺(tái)上,不知道名為“ECHO.PY”的文件會(huì)作為模塊echo、Echo還是ECHO被導(dǎo)入。(例如,Windows 95在顯示文件名時(shí)總是討厭地把第一個(gè)字母大寫)。DOS的8+3文件名限制更是對(duì)長(zhǎng)模塊名造成了有趣的困難。
這個(gè)問(wèn)題的唯一解決辦法是由模塊作者顯式地提供包的索引。引入*的import語(yǔ)句遵循如下規(guī)定:如果包的“__init__.py”文件定義了一個(gè)名為“__all__”的列表,這個(gè)列表就作為從包內(nèi)導(dǎo)入*時(shí)要導(dǎo)入的所有模塊的名字表。因此當(dāng)包的新版本發(fā)布時(shí)需要包的作者確保這個(gè)列表是最新的。包的作者如果認(rèn)為不需要導(dǎo)入*的話也可以不支持這種用法。例如,文件Sounds/Effects/__init__.py 可以包含如下代碼:
__all__ = ["echo", "surround", "reverse"]
這意味著from Sound.Effects import *將從Sound包中導(dǎo)入指定的三個(gè)子包?!?nbsp;
如果沒(méi)有定義__all__,則from Sound.Effects import *語(yǔ)句不會(huì)導(dǎo)入Sound.Effects包中的所有子模塊;此語(yǔ)句只能保證Sound.Effects被導(dǎo)入(可能是執(zhí)行其初始化代碼“__init__.py ”)并導(dǎo)入包中直接定義的名字。這包括由“__init__.py”定義的任何名字和顯式導(dǎo)入的子模塊名。這也包括模塊中已經(jīng)在前面用import顯式地導(dǎo)入的子模塊,例如:
import Sound.Effects.echo
import Sound.Effects.surround
from Sound.Effects import *
在這個(gè)例子中,echo和surround模塊被導(dǎo)入當(dāng)前名字空間,因?yàn)樗鼈冊(cè)趫?zhí)行from...import 語(yǔ)句時(shí)已定義(在定義了__all__的情況下這一點(diǎn)也是成立的)。
注意用戶應(yīng)盡量避免使用從模塊或包中導(dǎo)入*的做法,因?yàn)檫@樣經(jīng)常導(dǎo)致可讀性差的代碼。盡管如此,在交互運(yùn)行時(shí)可以用導(dǎo)入*的辦法節(jié)省敲鍵次數(shù),而且有些模塊在設(shè)計(jì)時(shí)就考慮到了這個(gè)問(wèn)題,它們只輸出遵循某種約定的名字。注意,from 包 import 特定子模塊的用法并沒(méi)有錯(cuò),實(shí)際上這還是我們推薦的用法,除非程序還需要用到來(lái)自其它包的同名的子模塊。
6.4.2 包內(nèi)部引用
子模塊常常需要彼此引用。例如,模塊surround可能要用到模塊echo。事實(shí)上,這樣的引用十分常見(jiàn),所以import語(yǔ)句首先從子模塊的所在包中尋找要導(dǎo)入的子模塊才在標(biāo)準(zhǔn)模塊搜索路徑查找。所以,模塊surround只要寫import echo或from echo import echofilter。如果在包含本模塊的包中沒(méi)有找到要導(dǎo)入的模塊,import語(yǔ)句將去尋找指定名字的頂級(jí)模塊。
當(dāng)包組織成子包時(shí)(比如例中的Sound包),沒(méi)有一種簡(jiǎn)單的辦法可以引用兄弟包中的子模塊――必須使用子模塊的全名。例如,如果模塊Sound.Filters.vocoder要引用Sound.Effects 包中的echo模塊,它可以用Sound.Effects import echo。
相關(guān)文章
Python利用Rasa框架和SMTPlib庫(kù)實(shí)現(xiàn)郵件回復(fù)助手
在現(xiàn)代辦公場(chǎng)景中,處理大量郵件是一項(xiàng)既耗時(shí)又容易出錯(cuò)的任務(wù),本文將詳細(xì)介紹如何使用Python的Rasa框架和SMTPlib庫(kù)建一個(gè)智能的郵件自動(dòng)回復(fù)助手,感興趣的可以了解下2025-04-04
通過(guò)數(shù)據(jù)庫(kù)向Django模型添加字段的示例
這篇文章主要介紹了通過(guò)數(shù)據(jù)庫(kù)向Django模型添加字段的示例,Django是人氣最高的Python web開發(fā)框架,需要的朋友可以參考下2015-07-07
python selenium 執(zhí)行完畢關(guān)閉chromedriver進(jìn)程示例
今天小編就為大家分享一篇python selenium 執(zhí)行完畢關(guān)閉chromedriver進(jìn)程示例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-11-11
python安裝numpy&安裝matplotlib& scipy的教程
下面小編就為大家?guī)?lái)一篇python安裝numpy&安裝matplotlib& scipy的教程。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-11-11
python實(shí)現(xiàn)DEM數(shù)據(jù)的陰影生成的方法
這篇文章主要介紹了python實(shí)現(xiàn)DEM數(shù)據(jù)的陰影生成的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-07-07
Jupyter notebook 更改文件打開的默認(rèn)路徑操作
這篇文章主要介紹了Jupyter notebook 更改文件打開的默認(rèn)路徑操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-05-05
Python之Matplotlib繪圖調(diào)節(jié)清晰度解決方案
Matplotlib是一個(gè)Python的繪圖庫(kù),可以用來(lái)繪制各種類型的圖表,包括線圖、散點(diǎn)圖、柱狀圖等等,這篇文章主要給大家介紹了關(guān)于Python之Matplotlib繪圖調(diào)節(jié)清晰度的相關(guān)資料,需要的朋友可以參考下2024-03-03

