模塊
函數(shù)可以復(fù)用代碼。如果想復(fù)用一系列的函數(shù)怎么辦?如你所想,答案就是模塊。
有很多寫(xiě)模塊的方法,但是最簡(jiǎn)單的還是創(chuàng)建一個(gè)后綴為.py的文件,然后在里面寫(xiě)上函數(shù)和變量。
另一種寫(xiě)模塊的方法:用本地語(yǔ)言(用來(lái)寫(xiě)Python解釋器的語(yǔ)言)寫(xiě)模塊。比如說(shuō),你可以用c語(yǔ)言來(lái)寫(xiě)模塊,經(jīng)過(guò)編譯后,你就可以通過(guò)Python解釋器使用它了。
其它程序可以導(dǎo)入模塊,然后使用模塊里的功能。就如我們使用Python的標(biāo)準(zhǔn)庫(kù)一樣。首先,我們學(xué)習(xí)如何使用標(biāo)準(zhǔn)庫(kù)里的模塊。
例子(保存至module_using_sys.py):
import sys
print('命令行參數(shù):')
for arg in sys.argv:
print(arg)
print('PYTHON_PATH:', sys.path)
輸出:
$ python module_using_sys.py we are arguments
命令行參數(shù):
module_using_sys.py
we
are
arguments
PYTHON_PATH: [.......]
它是怎么運(yùn)作的呢?
我們先是用import語(yǔ)句導(dǎo)入sys模塊,告訴Python,我們想要使用這個(gè)模塊。sys模塊包含關(guān)于Python解釋器和Python環(huán)境的功能。
當(dāng)Python執(zhí)行import sys時(shí),先是尋找sys模塊。因?yàn)閟ys模塊是內(nèi)置的,因此Python肯定知道在哪里能夠找到它。
如果不是編譯好的模塊,而是用Python寫(xiě)的模塊呢?Python解釋器會(huì)搜索一些目錄,去找要導(dǎo)入的模塊。這些目錄就是sys.path的列表。如果模塊找到了,模塊里的代碼就會(huì)被執(zhí)行,此刻開(kāi)始,模塊就生效了。注意,只有第一次導(dǎo)入一個(gè)模塊的時(shí)候,才會(huì)有上述的初始化過(guò)程。
sys模塊里的argv變量,可以用.后綴獲取到,也就是sys.argv。用這種點(diǎn)號(hào)后綴方式,清晰地表現(xiàn)出變量argv屬于sys模塊。這種方式另外一個(gè)優(yōu)點(diǎn)就是程序里的名字不會(huì)發(fā)生沖突。
sys.argv變量表示一個(gè)字符串列表。進(jìn)一步講,sys.argv包含命令行的參數(shù)值,也就是,在輸入執(zhí)行程序的命令時(shí),傳入的參數(shù)值。
如果你正在用IDE寫(xiě)程序,運(yùn)行程序,在菜單欄找到可以輸入命令行參數(shù)的選項(xiàng)。
當(dāng)執(zhí)行python module_using_sys.py we are arguments時(shí),只有python是命令,其它都是參數(shù)值,它們將會(huì)被保存到sys.argv變量里。
記住,運(yùn)行的腳本名字是參數(shù)值列表的第一個(gè)元素。所以在本例中,sys.argv[0]可以表示module_using_sys.py。sys.argv[1]表示we,sys.argv[2]表示are,sys.argv[3]表示arguments。注意,Python從0開(kāi)始計(jì)數(shù)。
sys.path包含一個(gè)目錄列表,這些目錄用來(lái)放置模塊??梢钥吹?,sys.path的列表的第一個(gè)元素是一個(gè)空字符串,它用來(lái)表示當(dāng)前目錄。也就是說(shuō),你可以直接導(dǎo)入當(dāng)前目錄里的模塊。其他情況下,你需要把模塊放到sys.path列表中目錄的一個(gè)。
注意,當(dāng)前目錄就是執(zhí)行程序時(shí)用戶所在的目錄。你可以導(dǎo)入os模塊,然后輸入print(os.getcwd())來(lái)找出當(dāng)前目錄。
byte-compiled .pyc文件
導(dǎo)入模塊的開(kāi)銷是挺大的,因此Python有一些技巧可以讓它更快。創(chuàng)建byte-compiled文件(后綴為.pyc)就是其中之一。byte-compiled文件是Python執(zhí)行程序時(shí)產(chǎn)生的中間代碼。這種文件在多個(gè)程序?qū)胂嗤哪K時(shí)很有用,因?yàn)閷?dǎo)入模塊的一些處理過(guò)程被跳過(guò)了。同樣,這些文件各個(gè)平臺(tái)都不一樣。
注意:這些.pyc文件一般生成在對(duì)應(yīng).py文件所在的目錄。如果Python在該目錄沒(méi)有寫(xiě)的權(quán)限,那么.pyc文件無(wú)法被創(chuàng)建。
from...import語(yǔ)句
如果想直接導(dǎo)入argv變量到你的程序里(不用每次都在前面加上sys.),你可以使用from sys import argv語(yǔ)句。
警告:
一般要避免使用from ... import語(yǔ)句,因?yàn)樗鼤?huì)增加名沖突的傾向,而且讓程序更難閱讀。
例子
from math import sqrt
print('16的平方根', sqrt(16))
模塊屬性name
每個(gè)模塊都有一個(gè)名字。在一個(gè)模塊里,可以用語(yǔ)句去找到所用模塊的名字。這個(gè)在查看模塊是作為腳本在運(yùn)行,還是被導(dǎo)入時(shí)相當(dāng)有用。當(dāng)一個(gè)模塊初次被導(dǎo)入,模塊內(nèi)的代碼被執(zhí)行。模塊作為腳本運(yùn)行和被導(dǎo)入的表現(xiàn)會(huì)有所差異。模塊的name會(huì)有所不同,就是其中之一。
例子(保存至module_using_name.py):
if __name__ == '__main__':
print('獨(dú)自運(yùn)行')
else:
print('被導(dǎo)入')
輸出:
$ python module_using_name.py
獨(dú)自運(yùn)行
$ python
>>> import module_using_name
被導(dǎo)入
它是怎么運(yùn)作的呢?
每一個(gè)模塊都會(huì)定義name變量。如果它被賦值__main__,就表示模塊作為腳本在運(yùn)行,這個(gè)時(shí)候你就可以搞點(diǎn)事了。
創(chuàng)建模塊
創(chuàng)建模塊很簡(jiǎn)單,你一直都在創(chuàng)建模塊。因?yàn)?,在Python中每一個(gè)程序都是一個(gè)模塊。你只要讓Python程序帶有.py后綴就可以了。
例子(保存至mymodule.py):
def say_hi():
print('哎,這就是模塊?')
__version__ = '0.1'
這是一個(gè)模塊樣例。所以,模塊和我們通常寫(xiě)的程序并沒(méi)有什么不同。下面將講解怎么在程序中使用模塊。
記住,模塊應(yīng)該放在程序運(yùn)行的目錄,或者sys.path列表中的某個(gè)目錄。
另外一個(gè)例子(保存至mymodule_demo.py):
import mymodule
mymodule.say_hi()
print('版本', mymodule.__version__)
輸出:
$ python mymodule_demo.py
哎,這就是模塊?
版本 0.1
它是怎么運(yùn)作的呢?
注意,我們使用點(diǎn)號(hào)去獲取模塊的成員。Python會(huì)在相似的場(chǎng)合下復(fù)用某種符號(hào),所以你不用去學(xué)習(xí)新的方式去完成任務(wù),這種風(fēng)格被稱為pythonic。
下面是使用from ...import的版本(保存至mymodule_demo2.py):
from mymodule import say_hi, __version__
say_hi()
print('版本', __version__)
輸出與運(yùn)行mymodule_demo.py的一樣。
注意,假設(shè)某個(gè)模塊有version名,如果再用from...import導(dǎo)入version會(huì)發(fā)生名沖突。這種情況很有可能發(fā)生,因?yàn)橥ǔ6际怯?strong>version用作版本號(hào)。因此,推薦使用import語(yǔ)句而不是使用from...import。
你也可以這樣寫(xiě):
from mymodule import *
這種情況會(huì)導(dǎo)入所有的公共名,但是以雙下滑線開(kāi)始的名不會(huì)被導(dǎo)入。
警告:
你應(yīng)該避免使用import *。
Python的禪:
一個(gè)Python的準(zhǔn)則是:明確的比隱晦的好。
dir函數(shù)
內(nèi)置的dir函數(shù)返回一個(gè)對(duì)象的名列表。如果對(duì)象是一個(gè)模塊,這個(gè)列表會(huì)包含在其中定義的函數(shù),類和變量。
這個(gè)函數(shù)接受參數(shù)值。如果參數(shù)是一個(gè)模塊的名,函數(shù)會(huì)返回一個(gè)模塊內(nèi)名的列表。如果沒(méi)有傳如參數(shù)值,函數(shù)返回當(dāng)前模塊的名列表。
例子:
$ python
>>> import sys
>>> dir(sys)
['__displayhook__',
'__doc__',
...,
]
>>> dir()
['__builtins__',
'__doc__',
...,
]
>>> a = 5
>>> dir()
['__builtins__',
'__doc__',
'sys',
'a',
...,
]
>>> del a
>>> dir()
['__builtins__',
'__doc__',
...,
]
它是怎么運(yùn)作的呢?
我們先是向dir傳入sys名。可以看到sys模塊包含了很多屬性。
接下來(lái),不給dir傳參數(shù)值。默認(rèn)情況,它會(huì)返回當(dāng)前模塊的所有屬性。注意,導(dǎo)入的模塊也會(huì)出現(xiàn)在列表里。
為了觀察dir的特性,我們定義了一個(gè)變量,并給它賦值,然后查看dir的結(jié)果,發(fā)現(xiàn)新增了一個(gè)同變量同名的元素。當(dāng)使用del把變量從當(dāng)前模塊移除后,dir打印出的結(jié)果中發(fā)生了相似的現(xiàn)象。
del的小提示:該語(yǔ)句用來(lái)刪除變量或名,在語(yǔ)句被執(zhí)行后,例子中的a變量被刪除了。你不再能訪問(wèn)a變量了,就好象之前沒(méi)有存在過(guò)。
注意,dir函數(shù)可以傳入任意對(duì)象名。舉個(gè)栗子,執(zhí)行dir(str)會(huì)返回str類的所有屬性。
有一個(gè)vars()函數(shù),會(huì)給出對(duì)象的屬性和它的值,但是它在某些情況下不起效。
--
包
--
你肯定觀察到了程序是有層次的。變量一般在函數(shù)里定義。函數(shù)和全局變量一般定義在模塊里。那么怎么組織模塊呢?答案就是包。
包就是包含模塊的文件夾,但是它會(huì)包含一個(gè)特殊的文件 - __init__.py。這個(gè)文件用來(lái)告知Python,該文件夾是一個(gè)包。
下面開(kāi)始創(chuàng)建一個(gè)叫做world的包,在里面包含asia包、africa包等等。這些子包包含一些模塊,像india、madagascar等等。
下面是目錄結(jié)構(gòu):
world/
__init__.py
asia/
__init__.py
india/
__init__.py
foo.py
africa/
__init__.py
madagascar/
__init__.py
bar.py
用包來(lái)組織模塊很便捷。你在標(biāo)準(zhǔn)庫(kù)里可以看到很多例子。
總結(jié)
待續(xù)。。。