鍍金池/ 教程/ Python/ 編寫模塊
標(biāo)準(zhǔn)庫 (4)
如何成為 Python 高手
標(biāo)準(zhǔn)庫 (6)
標(biāo)準(zhǔn)庫 (3)
類(2)
Pandas 使用 (2)
xml
用 tornado 做網(wǎng)站 (5)
文件(1)
練習(xí)
列表(3)
從小工到專家
除法
錯誤和異常 (2)
函數(shù)(1)
用 tornado 做網(wǎng)站 (7)
為做網(wǎng)站而準(zhǔn)備
函數(shù)練習(xí)
標(biāo)準(zhǔn)庫 (8)
Pandas 使用 (1)
回顧 list 和 str
字典(1)
用 tornado 做網(wǎng)站 (3)
字符串(1)
函數(shù)(2)
寫一個簡單的程序
將數(shù)據(jù)存入文件
語句(5)
SQLite 數(shù)據(jù)庫
集成開發(fā)環(huán)境(IDE)
集合(1)
類(1)
用 tornado 做網(wǎng)站 (6)
用 tornado 做網(wǎng)站 (2)
自省
語句(4)
錯誤和異常 (1)
用 tornado 做網(wǎng)站 (4)
集合(2)
列表(1)
標(biāo)準(zhǔn)庫 (1)
生成器
mysql 數(shù)據(jù)庫 (1)
第三方庫
實(shí)戰(zhàn)
運(yùn)算符
類(3)
字典(2)
語句(1)
數(shù)和四則運(yùn)算
語句(2)
文件(2)
MySQL 數(shù)據(jù)庫 (2)
電子表格
迭代器
mongodb 數(shù)據(jù)庫 (1)
特殊方法 (2)
特殊方法 (1)
字符編碼
編寫模塊
用 tornado 做網(wǎng)站 (1)
標(biāo)準(zhǔn)庫 (5)
函數(shù)(4)
類(5)
字符串(2)
關(guān)于 Python 的故事
函數(shù)(3)
字符串(4)
處理股票數(shù)據(jù)
常用數(shù)學(xué)函數(shù)和運(yùn)算優(yōu)先級
字符串(3)
為計(jì)算做準(zhǔn)備
多態(tài)和封裝
類(4)
迭代
語句(3)
錯誤和異常 (3)
分析 Hello
Python 安裝
標(biāo)準(zhǔn)庫 (2)
列表(2)
元組

編寫模塊

在本章之前,Python 還沒有顯示出太突出的優(yōu)勢。本章開始,讀者就會越來越感覺到 Python 的強(qiáng)大了。這種強(qiáng)大體現(xiàn)在“模塊自信”上,因?yàn)?Python 不僅有很強(qiáng)大的自有模塊(稱之為標(biāo)準(zhǔn)庫),還有海量的第三方模塊,任何人還都能自己開發(fā)模塊,正是有了這么強(qiáng)大的“模塊自信”,才體現(xiàn)了 Python 的優(yōu)勢所在。并且這種方式也正在不斷被更多其它語言所借鑒。

“模塊自信”的本質(zhì)是:開放。

Python 不是一個封閉的體系,是一個開放系統(tǒng)。開放系統(tǒng)的最大好處就是避免了“熵增”。

熵的概念是由德國物理學(xué)家克勞修斯于 1865 年(這一年李鴻章建立了江南機(jī)械制造總局,美國廢除奴隸制,林肯總統(tǒng)遇刺身亡,美國南北戰(zhàn)爭結(jié)束。)所提出。是一種測量在動力學(xué)方面不能做功的能量總數(shù),也就是當(dāng)總體的熵增加,其做功能力也下降,熵的量度正是能量退化的指標(biāo)。

熵亦被用于計(jì)算一個系統(tǒng)中的失序現(xiàn)象,也就是計(jì)算該系統(tǒng)混亂的程度。

根據(jù)熵的統(tǒng)計(jì)學(xué)定義, 熱力學(xué)第二定律說明一個孤立系統(tǒng)的傾向于增加混亂程度。換句話說就是對于封閉系統(tǒng)而言,會越來越趨向于無序化。反過來,開放系統(tǒng)則能避免無序化。

回憶過去

在本教程的《語句(1)》中,曾經(jīng)介紹了 import 語句,有這樣一個例子:

>>> import math
>>> math.pow(3,2)
9.0

這里的 math 就是一個模塊,用 import 引入這個模塊,然后可以使用模塊里面的函數(shù),比如這個 pow() 函數(shù)。顯然,這里我們是不需要自己動手寫具體函數(shù)的,我們的任務(wù)就是拿過來使用。這就是模塊的好處:拿過來就用,不用自己重寫。

模塊是程序

這個標(biāo)題,一語道破了模塊的本質(zhì),它就是一個擴(kuò)展名為 .py 的 Python 程序。我們能夠在應(yīng)該使用它的時候?qū)⑺眠^來,節(jié)省精力,不需要重寫雷同的代碼。

但是,如果我自己寫一個 .py 文件,是不是就能作為模塊 import 過來呢?還不那么簡單。必須得讓 Python 解釋器能夠找到你寫的模塊。比如:在某個目錄中,我寫了這樣一個文件:

#!/usr/bin/env Python
# coding=utf-8

lang = "python"

并把它命名為 pm.py,那么這個文件就可以作為一個模塊被引入。不過由于這個模塊是我自己寫的,Python 解釋器并不知道,我得先告訴它我寫了這樣一個文件。

>>> import sys
>>> sys.path.append("~/Documents/VBS/StartLearningPython/2code/pm.py")

用這種方式就是告訴 Python 解釋器,我寫的那個文件在哪里。在這個告訴方法中,也用了一個模塊 import sys,不過由于 sys 模塊是 Python 被安裝的時候就有的,所以不用特別告訴,Python 解釋器就知道它在哪里了。

上面那個一長串的地址,是 ubuntu 系統(tǒng)的地址格式,如果讀者使用的 windows 系統(tǒng),請寫你所保存的文件路徑。

>>> import pm
>>> pm.lang
'python'

本來在 pm.py 文件中,有一個變量 lang = "Python",這次它作為模塊引入(注意作為模塊引入的時候,不帶擴(kuò)展名),就可以通過模塊名字來訪問變量 pm.py,當(dāng)然,如果不存在的屬性這么去訪問,肯定是要報(bào)錯的。

>>> pm.xx
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute 'xx'

請讀者回到 pm.py 文件的存儲目錄,是不是多了一個擴(kuò)展名是 .pyc 的文件?如果不是,你那個可能是外星人用的 Python。

解釋器,英文是:interpreter,港臺翻譯為:直譯器。在 Python 中,它的作用就是將 .py 的文件轉(zhuǎn)化為 .pyc 文件,而 .pyc 文件是由字節(jié)碼(bytecode)構(gòu)成的,然后計(jì)算機(jī)執(zhí)行 .pyc 文件。關(guān)于這方面的詳細(xì)解釋,請參閱維基百科的詞條:直譯器

不少人喜歡將這個世界簡化簡化再簡化。比如人,就分為好人還壞人,比如編程語言就分為解釋型和編譯型,不但如此,還將兩種類型的語言分別貼上運(yùn)行效率高低的標(biāo)簽,解釋型的運(yùn)行速度就慢,編譯型的就快。一般人都把 Python 看成解釋型的,于是就得出它運(yùn)行速度慢的結(jié)論。不少人都因此上當(dāng)受騙了,認(rèn)為 Python 不值得學(xué),或者做不了什么“大事”。這就是將本來復(fù)雜的多樣化的世界非得劃分為“黑白”的結(jié)果。這種喜歡用“非此即彼”的思維方式考慮問題的現(xiàn)象可以說在現(xiàn)在很常見,比如一提到“日本人”,都該殺,這基本上是小孩子的思維方法,可惜在某個過度內(nèi)大行其道。

世界是復(fù)雜的,“敵人的敵人就是朋友”是幼稚的,“一分為二”是機(jī)械的。

就如同剛才看到的那個 .pyc 文件一樣,當(dāng) Python 解釋器讀取了 .py 文件,先將它變成由字節(jié)碼組成的 .pyc 文件,然后這個 .pyc 文件交給一個叫做 Python 虛擬機(jī)的東西去運(yùn)行(那些號稱編譯型的語言也是這個流程,不同的是它們先有一個明顯的編譯過程,編譯好了之后再運(yùn)行)。如果 .py 文件修改了,Python 解釋器會重新編譯,只是這個編譯過程不是完全顯示給你看的。

我這里說的比較籠統(tǒng),要深入了解 Python 程序的執(zhí)行過程,可以閱讀這篇文章:說說 Python 程序的執(zhí)行過程

總之,有了 .pyc 文件后,每次運(yùn)行,就不需要從新讓解釋器來編譯 .py 文件了,除非 .py 文件修改了。這樣,Python 運(yùn)行的就是那個編譯好了的 .pyc 文件。

是否還記得,我們在前面寫有關(guān)程序,然后執(zhí)行,常常要用到 if __name__ == "__main__"。那時我們寫的 .py 文件是來執(zhí)行的,這時我們同樣寫了 .py 文件,是作為模塊引入的。這就得深入探究一下,同樣是 .py 文件,它是怎么知道是被當(dāng)做程序執(zhí)行還是被當(dāng)做模塊引入?

為了便于比較,將 pm.py 文件進(jìn)行改造,稍微復(fù)雜點(diǎn)。

#!/usr/bin/env Python
# coding=utf-8

def lang():
    return "Python"

if __name__ == "__main__":
    print lang()

如以前做的那樣,可以用這樣的方式:

$ Python pm.py
python

但是,如果將這個程序作為模塊,導(dǎo)入,會是這樣的:

>>> import sys
>>> sys.path.append("~/Documents/VBS/StarterLearningPython/2code/pm.py")
>>> import pm
>>> pm.lang()
'python'

因?yàn)檫@時候 pm.py 中的函數(shù) lang() 就是一個屬性:

>>> dir(pm)
['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'lang']

同樣一個 .py 文件,可以把它當(dāng)做程序來執(zhí)行,還可以將它作為模塊引入。

>>> __name__
'__main__'
>>> pm.__name__
'pm'

如果要作為程序執(zhí)行,則__name__ == "__main__";如果作為模塊引入,則 pm.__name__ == "pm",即變量__name__的值是模塊名稱。

用這種方式就可以區(qū)分是執(zhí)行程序還是作為模塊引入了。

在一般情況下,如果僅僅是用作模塊引入,可以不寫 if __name__ == "__main__"。

模塊的位置

為了讓我們自己寫的模塊能夠被 Python 解釋器知道,需要用 sys.path.append("~/Documents/VBS/StarterLearningPython/2code/pm.py")。其實(shí),在 Python 中,所有模塊都被加入到了 sys.path 里面了。用下面的方法可以看到模塊所在位置:

>>> import sys
>>> import pprint
>>> pprint.pprint(sys.path)
['',
 '/usr/local/lib/python2.7/dist-packages/autopep8-1.1-py2.7.egg',
 '/usr/local/lib/python2.7/dist-packages/pep8-1.5.7-py2.7.egg',
 '/usr/lib/python2.7',
 '/usr/lib/python2.7/plat-i386-linux-gnu',
 '/usr/lib/python2.7/lib-tk',
 '/usr/lib/python2.7/lib-old',
 '/usr/lib/python2.7/lib-dynload',
 '/usr/local/lib/python2.7/dist-packages',
 '/usr/lib/python2.7/dist-packages',
 '/usr/lib/python2.7/dist-packages/PILcompat',
 '/usr/lib/python2.7/dist-packages/gtk-2.0',
 '/usr/lib/python2.7/dist-packages/ubuntu-sso-client',
 '~/Documents/VBS/StarterLearningPython/2code/pm.py']

從中也發(fā)現(xiàn)了我們自己寫的那個文件。凡在上面列表所包括位置內(nèi)的 .py 文件都可以作為模塊引入。不妨舉個例子。把前面自己編寫的 pm.py 文件修改為 pmlib.py,然后把它復(fù)制到'/usr/lib/Python2.7/dist-packages 中。(這是以 ubuntu 為例說明,如果是其它操作系統(tǒng),讀者用類似方法也能找到。)

$ sudo cp pm.py /usr/lib/python2.7/dist-packages/pmlib.py
[sudo] password for qw: 

$ ls /usr/lib/python2.7/dist-packages/pm*
/usr/lib/Python2.7/dist-packages/pmlib.py

文件放到了指定位置??聪旅娴模?/p>

>>> import pmlib
>>> pmlib.lang
<function lang at 0xb744372c>
>>> pmlib.lang()
'python'

也就是,要將模塊文件放到合適的位置——就是 sys.path 包括位置——就能夠直接用 import 引入了。

PYTHONPATH 環(huán)境變量

將模塊文件放到指定位置是一種不錯的方法。當(dāng)程序員都喜歡自由,能不能放到別處呢?當(dāng)然能,用 sys.path.append() 就是不管把文件放哪里,都可以把其位置告訴 Python 解釋器。但是,這種方法不是很常用。因?yàn)樗灿新闊┑牡胤剑热缭诮换ツJ较?,如果關(guān)閉了,然后再開啟,還得從新告知。

比較常用的告知方法是設(shè)置 PYTHONPATH 環(huán)境變量。

環(huán)境變量,不同操作系統(tǒng)的設(shè)置方法略有差異。讀者可以根據(jù)自己的操作系統(tǒng),到網(wǎng)上搜索設(shè)置方法。

我以 ubuntu 為例,建立一個 Python 的目錄,然后將我自己寫的 .py 文件放到這里,并設(shè)置環(huán)境變量。

:~$ mkdir Python
:~$ cd python
:~/Python$ cp ~/Documents/VBS/StarterLearningPython/2code/pm.py mypm.py
:~/Python$ ls
mypm.py

然后將這個目錄 ~/Python,也就是 /home/qw/Python 設(shè)置環(huán)境變量。

vim /etc/profile

提醒要用 root 權(quán)限,在打開的文件最后增加 export PATH = /home/qw/python:$PAT,然后保存退出即可。

注意,我是在 ~/Python 目錄下輸入 Python,進(jìn)入到交互模式:

:~$ cd Python
:~/python$ Python

>>> import mypm
>>> mypm.lang()
'Python'

如此,就完成了告知過程。

__init__.py 方法

__init__.py 是一個空文件,將它放在某個目錄中,就可以將該目錄中的其它 .py 文件作為模塊被引用。這個具體應(yīng)用參見用 tornado 做網(wǎng)站(2)


總目錄   |   上節(jié):錯誤和異常(3)   |   下節(jié):標(biāo)準(zhǔn)庫(1)

如果你認(rèn)為有必要打賞我,請通過支付寶:qiwsir@126.com,不勝感激。