簡介
現在你已經知道通過定義函數可以在你的程序中復用代碼。但當你想在你編寫的其他程序中復用大量函數怎么辦呢?
也許你可以猜到了,辦法就是利用模塊。
有各種編寫模塊的方式,但最簡單的方式是創建一個以.py為后綴的文件並包含所需的函數與變量。
另一種方式是以編寫python解釋器的本地語言編寫模塊。
例如C語言編寫的模塊被編譯后可供運行於標准python解釋器上的python代碼使用。
模塊可以被其它程序導入以使用其提供的功能。這也是為什么我們可以使用python標准庫。
我們先來看看如何使用標准庫模塊。
范例:
#!/usr/bin/python
# Filename: using_sys.py
import sys
print('The command line arguments are:')
for i in sys.argv:
print(i)
print('/n/nThe PYTHONPATH is', sys.path, '/n')
輸出:
$ python using_sys.py we are arguments
The command line arguments are:
using_sys.py
we
are
arguments
The PYTHONPATH is ['', 'C://Windows//system32//python30.zip',
'C://Python30//DLLs', 'C://Python30//lib',
'C://Python30//lib//plat-win', 'C://Python30',
'C://Python30//lib//site-packages']
工作流程:
首先我們使用import語句導入sys模塊。本質上這告訴python我們希望使用這個模塊。
sys模塊包含python解釋器與其工作環境(即系統)相關的功能。
當python執行import sys語句時,它將查找sys模塊。本例中sys是內建模塊之一,因此python知道在哪能找到它。
如果導入的不是一個編譯模塊,即不是用python編寫的模塊,python解釋器會在變量sys.path中列出的目錄中查找它。
(注:if it was not a compiled module i.e. a module written in Python)
如果模塊被找到,這個模塊中的語句將被執行然后你就可以使用它了(注: 只有頂級語句才會執行,也就是主塊中的語句)。
注意一個模塊只有在第一次導入時會被初始化。
sys模塊中的argv通過點號引用即sys.argv。它清晰的指出這個名字是sys模塊中的一部分。
這種語法的另一個優勢是不會和你的程序中的同名argv變量發生沖突。
變量sys.argv是一個字符串列表(后章會詳細解釋列表)。
具體說sys.argv是一個包含命令行參數的列表,也就是使用命令行傳遞給你的程序的參數。
如果你在使用IDE編寫程序,請在菜單中查找為程序指定命令行參數的方法。
這里,當我們執行python using_sys.py we are arguments時,我們以python命令運行using_sys.py模塊,其后的內容是傳遞給程序的參數。
python將它們存到sys.argv以供我們使用。
記住,被運行腳本的腳本名永遠是sys.argv的第一個參數。
所以本例中sys.argv[0]為’using_sys.py’,sys.argv[1]為’we’,sys.argv[2]是’are’, argv[3]是’arguments’。注意python下標從0開始而非1。
sys.path包含一個目錄名列表指示從哪里導入模塊。
觀察程序輸出,sys.path的第一個字符串為空 – 其指出當前目錄也是sys.path的一部分,這與PYTHONPATH環境變量是相同的。
這意味着你可以直接導入當前目錄下的模塊,否則你就必須將你的模塊放到sys.path列出的目錄中的一個了。
注意程序在哪個目錄運行的,這個目錄就是這個程序的當前目錄。運行import os; print(os.getcwd()) 可以看到你的程序的當前目錄。
(注:windows下sys.path[0]可能不為空,而是顯式指出當前路徑)
字節編譯文件 .pyc
導入模塊是一個相對昂貴的操作,所以python使用了一些技巧加速這個過程。
一個辦法是創建后綴為.pyc的字節編譯文件用於將程序轉換為中間格式。(還記得介紹python如何工作的那一節嗎?)
當你下次從其他文件導入模塊時pyc文件會非常有用 – 它將大大增加導入速度,因為導入模塊的部分操作已經預先完成了。
並且這個字節編譯文件仍然是平台無關的。
注意
.pyc文件一般被創建在與其對應的.py文件所在的相同目錄下。如果python沒有這個目錄的寫權限,則.pyc文件不會被創建。
from…import…語句
如果你希望將變量argv直接導入到你的程序中(避免每次輸入sys.),那么可以使用from sys import argv語句。
如果希望導入sys模塊中的所有名字,則from sys import *可以做到。此語句可以用於任何模塊。
通常你應該避免使用這個語句並用import語句代替之,因為使用后者可以避免名字沖突,程序的可讀性也更好。
模塊的__name__屬性
每個模塊都有一個名字,並且通過模塊中的某些語句可以得到這個模塊名。
在一些想要搞清模塊是獨立運行還是被導入的情況下,這會非常方便。
如前所述當模塊第一次被導入時模塊中的代碼會被執行。我們可以據此改變模塊獨立執行時的行為方式。
這可以通過模塊的__name__屬性做到。(注:獨立運行是指程序最開始運行的那個腳本文件(/模塊))
范例:
#!/usr/bin/python
# Filename: using_name.py
if __name__ == '__main__':
print('This program is being run by itself')
else:
print('I am being imported from another module')
輸出:
$ python using_name.py
This program is being run by itself
$ python
>>> import using_name
I am being imported from another module
>>>
工作流程:
每個python模塊都有自己的__name__定義,如果它是’__main__’則暗示模塊為獨立運行,我們可以進行一些適當的處理。
制作你自己的模塊
創建你自己的模塊很簡單,其實你一直在這樣做!因為每個python腳本都是一個模塊。你只需確保它帶有.py擴展名即可。
下面的例子會讓你對其有一個清晰的認識:
范例:
#!/usr/bin/python
# Filename: mymodule.py
def sayhi():
print('Hi, this is mymodule speaking.')
__version__ = '0.1'
# 結束mymodule.py編寫
上面就是一個簡單的模塊,如你所見,這和我們平時的python程序相比沒有什么特別之處。
記住模塊應該放到導入它的那個程序所在的目錄下,或者放到sys.path列出的目錄之一中。
#!/usr/bin/python
# Filename: mymodule_demo.py
import mymodule
mymodule.sayhi()
print ('Version', mymodule.__version__)
輸出:
$ python mymodule_demo.py
Hi, this is mymodule speaking.
Version 0.1
如何工作:
注意我們同樣使用點號訪問模塊成員。
python很好的重復利用了相同的符號,帶來獨特的’Pythonic’感受,這樣我們就不必學習更多的語法知識了。
下面是一個使用from…import語法的版本:
#!/usr/bin/python
# Filename: mymodule_demo2.py
from mymodule import sayhi, __version__
sayhi()
print('Version', __version__)Python en:Modules 59
mymodule_demo2.py與mymodule_demo的輸出完全相同。
注意,如果導入mymodule的模塊中已經存在同名的__version__,則將發生名字沖突。
事實上這很可能發生,因為每個模塊都用__version__聲明它的版本是一種常見的做法。
因此建議你優先考慮import語句,雖然它可能會讓你的程序變的更長一些。
你同樣可以使用:
from mymodule import *
這將導入模塊的所有公有名字,例如sayhi,但是不會導入__version__因為它以雙下划線開頭。
Python之禪
python的一個指導原則是”清晰的好過隱晦的”. 執行import this可以看到完整內容。
這里的討論列出了每個原則的范例(http://stackoverflow.com/questions/228181/zen-of-python)
dir函數
你可以使用dir函數列出一個對象定義的所有標識符。例如對於一個模塊,標識符包括函數,類,變量。
當你為dir()函數提供一個模塊名,它將返回定義在其中的所有名字。
當dir()的參數為空時,返回定義在當前模塊中所有名字。
范例:
$ python
>>> import sys # 得到sys模塊的屬性列表
>>> dir(sys)
['__displayhook__', '__doc__', '__excepthook__', '__name__',
'__package__', '__stderr__', '__stdin__', '__stdout__', '_clear_type_cache',
'_compact_freelists', '_current_frames', '_getframe', 'api_version', 'argv',
'builtin_module_names', 'byteorder', 'call_tracing', 'callstats', 'copyright', 'displayhook',
'dllhandle', 'dont_write_bytecode', 'exc_info', 'excepthook', 'exec_prefix', executable',
'exit', 'flags', 'float_info', 'getcheckinterval', 'getdefaultencoding', 'getfil
esystemencoding', 'getprofile', 'getrecursionlimit', 'getrefcount', 'getsizeof',
'gettrace', 'getwindowsversion', 'hexversion', 'intern', 'maxsize', 'maxunicode',
'meta_path', 'modules', 'path', 'path_hooks', 'path_importer_cache', 'platform',
'prefix', 'ps1', 'ps2', 'setcheckinterval', 'setprofile', 'setrecursionlimit',
'settrace', 'stderr', 'stdin', 'stdout', 'subversion', 'version', 'version_info', 'warnoptions', 'winver']
>>> dir() # 得到當前模塊的屬性列表
['__builtins__', '__doc__', '__name__', '__package__', 'sys']
>>> a = 5 # create a new variable 'a'
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'a', 'sys']
>>> del a # 刪除名字a
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'sys']
工作流程:
首先,我們通過被導入的sys模塊應用dir函數. 可以看到sys包含巨多的屬性。
接下來,我們不為dir函數提供參數。默認的它返回當前模塊的屬性列表。注意被導入模塊列表也是當前模塊列表的一部分。
為了觀察到dir確定起作用了,我們定義一個新變量a並為其賦值然后檢驗dir的返回值,我們發現在返回的列表中確實出現
了一個與變量a同名的值。在我們使用del語句刪除當前模塊的變量/屬性后,改變再次反映到了dir函數的輸出上。
del注解 – 這個語句用於刪除一個變量/名字,在本例中,del a之后你就無法訪問變量a了 – 就像它從來沒有存在過一樣。
注意dir()函數可用於任何對象。例如執行dir (print)學習更多關於print函數的屬性,或是dir(str)列出str類的屬性。
包
如今,你必須開始留心組織你的程序層次了。
變量在函數內部,函數和全局變量通常在模塊內部。那么如何組織模塊呢?這就輪到包登場了。
包僅僅是包含模塊的文件夾,並帶有一個特殊的文件__init__.py用於指示python這個文件夾是特殊的,因為它包含python模塊。
讓我們假設你需要創建一個叫做’world’的包,里面包括諸如’asia’,‘africa’等的子包。
下面告訴你應該如何組織文件夾結構:
- <some folder present in the sys.path>/
- world/
- __init__.py
- asia/
- __init__.py
- india/
- __init__.py
- foo.py
- africa/
- __init__.py
- madagascar/
- __init__.py
- bar.py
包只是用來有層次的組織模塊。你會在標准庫中看到它的很多應用。
小結
就象函數是程序的可復用部分一樣,模塊是可復用的程序。
包用於組織模塊的層次結構。python自帶的標准庫正是一組包和模塊的范例。
我們已經看到如何使用這些模塊,並且知道如何創建自己的模塊。
接下來,我們將學習一個比較有趣的概念 – 數據結構。

