面向初學者介紹Python相關的一些工具,以及可能遇到的常見問題。
最后更新 2013.5.26
之前有兩篇同名的文章發在網上,在這里編輯整合在一起放在這里。
相關文章 -Python 四五事
引言
在這里我假設你已經看完了一篇Python教程,基本熟悉了Python的結構和語法,在命令行下的Python互動環境中嘗試過大部分Python的語句,覺得Python是個不錯的語言準備繼續下去。那么本篇文章會就Python實際運用中相關工具的選擇,包括IDE,調試套件,第三方庫管理工具這些進行介紹。另外還會對某些中文環境下容易遇到的問題,例如unicode編碼解碼的問題進行說明。本文主要是針對 Windows 環境下的 Python 開發進行說明。文章的目的是為了分享些我覺得很有用的經驗和例子,若發現文中有疏漏之處請務必聯系我。謝謝。
Python 語言介紹
Python 是一個近些年在開始流行起來的計算機編程語言。根據Python官網上的簡介,Python主要特性包括跨平臺,免費,簡單且容易維護。就我個人理解來說,Python是一門適合大部分人的語言,因為各種類型的第三方庫都有,所以像簡單桌面程序,動態網站開發,圖像處理,表格處理,甚至自動發帖機這些小應用在簡單的學習后,不需要很深厚的編程經驗的人應該都能自己做出來。
一些流行的Python教程有:
Dive into Python面向有一定編程基礎的同學。另外還有Dive into Python 3,針對Python3的教程。
Learn Python The Hard Way,書中主要是通過各種練習來進行學習,面向完全沒有編程經驗的同學。
Invent Your Own Computer Game With Python,讓你一上手就做個游戲出來的教程,厲害吧。
The Python Tutorial,官方文檔中的教程,正統而完整。
如果你還沒有開始接觸 Python ,或者覺得還不夠熟悉,那么不妨找一份你覺得看得下去的教程開始學習吧。就我個人經驗來說,Python 是我到目前為止覺得學的最劃得來的一門語言,也是日常用的最多的一門,而事實上你并不需要了解完全了解 Python 就能在開始使用它。
Python 版本選擇,其他發行版
Python 2 與 3
Python 2 和 3 系列的選擇可能是比較讓人煩躁的事情。其實區別很簡單:Python 3.x 各個方面都更好,但語法與 Python 2.x 很大部分不兼容。Python 2.x 已經停止繼續開發。但是目前很多第三方庫仍然不支持 Python 3 , 文章后面介紹的很多工具,特別是科學計算的庫仍然只支持 Python 2.x。
原來這里寫的推薦版本是 2.6,現在的情況是 2.7 版本是 2.x 系列最后一個大版本號,以后只會有 2.7.x 的維護版本并不再會添加新功能。Python 3 現在已經足夠成熟,很多常見的庫都已經移植到 Python 3 上了。所以現在如果你是自己學 Python 那么上來直接 Python 3 沒啥問題,但如果你是在工作的地方或者對科學計算有要求那么還是需要 Python 2.7 比較好。
這里我發現有一個比較靠譜的選擇辦法,就是看 Python(x,y) 帶的 Python 版本。它現在還是在 2.7,這個版本基本上所有的第三方庫都在支持。當哪一天 Python(x,y) 上到 Python 3 了那估計就是可以安心用 Python 3 的時候了。但按現在這個情況估計還得好幾年吧。
發行版
目前在 Windows 下除了官方提供的安裝版外,還有:
ActivePython,這個與官方版本的區別在于提供了額外的庫和文檔,并且自動設置了PATH環境變量(后文會詳細提到)
Python(x,y),這個是我一直用并且推薦給別人用的版本。從名字就能看出來這個發行版附帶了科學計算方面的很多常用庫,另外還有大量常用庫比如用于桌面軟件界面制作的PyQt, 還有文檔處理,exe文件生成等常用庫。另外的還有大量的工具如IDE,制圖制表工具,加強的互動shell之類。很多下文提到的軟件在此發行版中都有附帶。其他方面,Python(x, y)還附帶了手工整理出的所有庫的離線文檔,每個小版本升級都提供單獨的補丁。總的來說是很用心維護的一個發行版,十分建議安裝這個版本。
Conda, 一個類似 Python(x, y) 的輕量級發行版。他支持的庫稍微少一點但是常用的也基本包括了,它提供了一個額外的包裹管理命令還支持 Python 3.x,同時提供簡潔版的Miniconda供自己手動選擇庫進行下載。
開發相關工具
首先,你需要一份文檔
對于 Python 這樣的語言,你覺得你學到什么時候算是完全掌握呢?你也許會想也許哪一天你記得大部分函數的名字很怎么用,不用打幾行就 Google 搜一下的時候,就算學會了。這樣的理解對了一半,等你熟悉 Python 以后你的卻不應該常搜索;但前一半卻不一定,我個人認為你并不需要記住龐大的標準庫中的內容,很多時候你只要清楚要在哪里能找到相應的文檔就行了。
Python 在這方面可以說是做的非常非常非常好。在真正著手開發之前,你應該在下載一份離線的文檔。在這個頁面(如果打不開的話試試這里,你懂的)下載一份 HTML 格式的,比如是 2.7.3 版那么對應的文檔名字應該是python-2.7.3-docs-html.zip。下好后把它在一個你喜歡的地方解壓出來,打開其中的index.html,這就是這個文檔的主頁。你可以看到他分為很多部分,包括語言的參考,標準庫和其他很多方便的文檔。
如果你一下不知道從哪里看起,這份文檔還有一個非常棒的功能。看到左邊的Quick Search欄,我在上圖中也有標注起來。當你需要對某個函數或者標準庫進行進一步了解的時候,你可以在這邊來進行搜索。這里的搜索是火星科技驅動的離線狀態下也能夠使用的!比如輸入urllib.urlencode,你可以很方便的找到它對應的頁面。基本有了這份文檔,你可以避免掉很多瘋狂搜索的情況。同樣的,當你使用某個第三方庫的時候,你最好也在他的站點上找找有沒有一份離線文檔,因為 Python 項目很多都有著很贊的文檔。
開發環境的選擇
如果你選擇用 IDE 的話,現在的選擇就非常多了,包括PyCharm,Python Tools for Visual Studio。你也可以硬派一點用一個文本編輯器直接寫。但我強烈建議不要使用 Python 發行版自帶的那個 IDLE。開發者沒有真的認為誰會仔細用那它,一直以來沒有什么新功能,運行效率不行,而且會出詭異的問題(比如會報什么 socket 錯誤)。希望你花些時間找個順手的工具,只要不是 IDLE 就可以。
選擇 PyDev 作為 IDE
Python 集成開發環境的選擇好像一直以來也是一個很難抉擇的問題。在嘗試過很多個工具后我發現基于 Eclipse 的PyDev絕對是功能最為完整的一個 IDE 。除了斷點調試之外,PyDev 的代碼自動補全可能是現在這類 IDE 中最強力的。
如果你安裝了Python(x, y)的話,PyDev 就已經在你的機器上了。如果沒有的話請按照這篇文章來進行安裝。
設置上有一些需要注意的地方。首先在打開 PyDev ,打開菜單中Window -> Preferences,在彈出對話框中左邊找到PyDev -> Editor -> Code Completion。這里可以設置代碼自動補全的相關信息。可以降低 Autocompletion delay 來更早的提示代碼,并且將 Request completion on 系列盡可能勾上,讓 PyDev盡可多的提示代碼。
之后再找到Interpreter Python選項卡,這里可以設置所謂 Forced Buildins,可以強制引入某些第三方庫從而完成代碼補全。就我的經驗來看大部分第三方庫在這樣設置后都能進行基本的補全。具體的做如圖中,選擇到對應的選項卡,點擊 New,并輸入你需要的模塊名字即可。
設置后總體效果絕對是同類IDE中比較好的:
IPython 替代 Python Shell
在學習 Python 的時候應該都接觸過 Python 的 Shell,能夠輸入 Python 語句并且立即返回結果。而IPython就是一個豪華加強版的 Python Shell。如果你安裝了 Python(x, y) 的話,那 IPython 已經在你的機器上了。如果沒有的話那么請在這里下載 Windows Installer進行安裝。在安裝這個之后還需要安裝pyreadline讓 IPython 開啟高亮和自動補全功能。
之后你在命令行下需要 python 的時候改為輸入ipython就能使用它了。開啟 IPython 看看,首先感覺的不同應該是這個是有顏色的。我們來看看它提供的一些基礎而實用的功能吧。首先是自動補全,一種是簡單的關鍵字補全,另外一種是對象的方法和屬性補全。作為例子,我們先引入sys模塊,之后再輸入sys.(注意有個點),此時按下 tab 鍵,IPython 會列出所有 sys 模塊下的方法和屬性。因為是在互動模式下進行的,此時的 Python 語句實實在在的被執行了,所以對普通 object 的補全也是很完好的。
接著上面的例子,我們輸入sys?,這樣會顯示出sys模塊的 docstring及相關信息。很多時候這個也是很方便的功能。
IPython 實用技巧
這里再介紹下 IPython 使用中的一些實用功能。在學習 Python 時你可能看到在循環或者函數返回時可以賦值給_來表示忽略某個返回值。其實這只是一個常用的習慣。事實上_是一個合法的變量名,而且在 Python shell 下_總是被賦予之前最后一個輸出的值。這里看個例子應該就能清楚:
>>> import string
>>> string.letters
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
>>> print _
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
舉個實際的例子,比如你在調試時讀文件的時候直接進行f.read(),你看了看發現輸出結果很有意思,想要對它進行進一步處理,但發現讀的時候忘記賦值了。以往你只能嘆嘆氣重新開文件再讀一次,現在你只要執行result = _,把_附到另外一個變量就可以了。
IPython 還有強大之處很大部分還體現在它的magic function中。它是指的在 IPython 環境下執行以%開頭的一些命令來對 IPython 進行一些設定或者執行某些功能。在 IPython 中輸入%lsmagic就能列出所有的magic functions。在這里簡單介紹下幾個比較有意思的,你也可以自己通過查看文檔來找找有哪些你特別用的到得。
之前看到能用?來查詢函數的文檔,對于 magic function 也是如此。比如%run?。
!cd ..在命令前面加上!則它會被作為命令行命令執行,這樣你就不用退出 IPython 來進行命令行操作。
%run foo.py在當前環境下直接執行foo.py,效果跟命令行下調用ipython foo.py相同。
%time foo.bar()跟timeit decorator作用相同,進行簡單的 profile。
%hist能顯示之前輸入過的命令的歷史,同時你可以用In[]來訪問之前的命令。比如%exec In[10]就能執行列表中第十行。
%rep類似上面的_變量,但是是以字串的形式返回
最后,如果%automagic是打開的狀態的話,所有 magic function 不需要在前面加%就能正確調用。
在當前 IPython 版本中還有一個由于安全原因沒有默認引入的%autoreload,它的作用是在可以自動重新載入你調用的函數,以及其相關模塊。接觸過django的同學對這個應該比較熟悉,在 IPython 中的效果就是,當你在調試一個一直在反復改動的函數時,你可以開啟這個功能保證每次調用都會重新讀取最新的版本,讓你在源碼中的改動馬上生效。在 IPython 中執行
import ipy_autoreload
%%autoreload 2
這樣 IPython 會對所有的模塊都進行 autoreload。你可以通過執行%autoreload?來查詢它的文檔來進行進一步設定。如果你希望 IPython 每次啟動自動載入次功能,那么可以通過配置 ipythonrc (在 Windows 下可以在C:\Users\\_ipython\ipythonrc.ini找到) 來進行相關設置。
最后還有一個很神奇的功能。如果你的程序是由命令行開始執行的,即在命令行下輸入python foo.py(大部分 Python 程序都是),那么你還可以利用 IPython 在你的程序任意地方進行斷點調試!在你程序中任意地方,加入如下語句:
from IPython.Shell import IPShellEmbed
IPShellEmbed([])()
注意:最近 IPython 發布了0.11 版本,各方面變化都非常大,API 也經過了重新設計。如果你使用的是 0.11 那么上面兩行對應的是這樣的:
from IPython import embed
embed()
再和平常一樣運行你的程序,你會發現在程序運行到插入語句的地方時,會轉到 IPython 環境下。你可以試試運行些指令,就會發現此刻 IPython 的環境就是在程序的那個位置。你可以逐個瀏覽當前狀態下的各個變量,調用各種函數,輸出你感興趣的值來幫助調試。之后你可以照常退出 IPython,然后程序會繼續運行下去,自然地你在當時 IPython 下執行的語句也會對程序接下來的運行造成影響。
這個方法我實在這里看到的。想象一下,這樣做就像讓高速運轉的程序暫停下來,你再對運行中的程序進行檢查和修改,之后再讓他繼續運行下去。這里舉一個例子,比如編寫網頁 bot ,你在每取回一個頁面后你都得看看它的內容,再嘗試如何處理他獲得下一個頁面的地址。運用這個技巧,你可以在取回頁面后讓程序中斷,再那里實驗各種處理方法,在找到正確的處理方式后寫回到你的代碼中,再進行下一步。這種工作流程只有像 Python 這種動態語言才可以做到。
pip 管理第三方庫
Python 的一大優勢就是有極為大量的第三方庫,包括各個方面的引用。然而安裝第三方庫對沒有掌握方法的同學來說會變得很讓人煩惱。事實上 Python 第三方庫的安裝和管理有著一個一個唯一正確的做法,這個做法要求你什么其他的都不用干,只要輸入你要安裝庫的名字就可以了。
setuptools 也包在 Python(x, y) 當中。如果沒有的話,要首先先安裝setuptools,這個其實就是一個安裝第三方庫的軟件。選擇對應版本的 Windows Installer 進行下載和安裝后,打開一個命令行窗口,輸入:
easy_install pip
如果提示找不到程序,那么說明你當前沒有設定好環境變量。安裝官方提供的 Python 安裝包的話肯定會有這個問題,而且很可能暫時不會修正,這就是牛逼程序員的倔強。具體做法是 右鍵我的電腦 - 屬性 - 高級系統設置 - 環境變量 - 將C:\python2*\Scripts加入到 PATH 那一組當中。這樣做的效果就是在任何地方的命令行下輸入命令,那么系統會額外查找我們設定的那個目錄中的內容。之后再執行上面的命令,裝好了以后我們就要棄用 setuptools,轉投pip。要安裝任何一個庫,你只要找到他的名字(不需要版本號),用 pip 安裝即可。譬如安裝django,那么輸入如下命令即可:
pip install django
其實之前easy_install跟pip效用是類似的,都是在官方的第三方庫索引PyPI查詢信息并進行下載和安裝。pip 的優勢在于支持更高級的功能,譬如虛擬環境,安裝失敗不會殘留破損的庫,更重要的是 pip 還可以進行卸載。輸入下面命令就能卸載一個之前由 pip 進行安裝的庫。繼續上面的例子,現在要卸載 django:
pip uninstall django
這是 setuptools 所缺失的功能。需要額外說明的是大部分純 Python 的庫都能用這個方法在 Windows 下裝上,但是需要編譯 C 語言模塊的一般都不太可能成功。遇到這種情況,在相應的庫德站點上找找有沒有對應的 Windows 安裝包。
用 virtualenv 構建虛擬 Python 環境
如果你使用過 Python 做過 Web 開發,或者你有需求在本機上安裝多個版本的 Python 來測試你的代碼能否跑再 2.5, 2.6, 2.7 各個版本上,或者你的不同項目依賴于一個第三方庫的不同版本;再或者,有時候你就是想要一個沒有之前安裝過的亂七八糟的庫,一個干凈的 Python 環境。這種時候virtualenv就能幫上你的忙。它能利用安裝好的 Python ,在同一臺機器上建立一個或多個互不相干的虛擬 Python 環境,且能隨時切換。如果你看到這里還不覺得這個有什么用處,那不妨看下去留下點印象,等哪天你有這類需求的時候能找到這個簡單實用的工具。
和其他第三方庫一樣,我們可以通過 pip 輕松安裝:
pip install virtualenv
安裝完成后你可以開啟一個命令行窗口,輸入virtualenv看看能不能找到這個腳本。如果有問題的話,請按照上面介紹過的步驟檢查下是否設置好了 PATH。之后我們可以在一個方便的地方建立一個虛擬環境。建立C:\envs\文件夾,命令行下 cd 到該文件夾中,輸入:
virtualenv --no-site-packages --python=C:\Python26\python.exe envtest
之后應該會看到一個叫envtest的文件夾。這就是一個新建立的虛擬環境(virtual enviroment)。我們不妨先激活它來看看應該怎么用。命令行下執行envtest\Scripts\activate.bat,這時是你會發現命令行變成這個樣子:
(envtest) c:\>
提示符前面的(envtest)就是該環境已被激活的標志。這樣你就可以在這個虛擬環境下進行工作了。執行pip freeze,你會發現... 你會發現什么都沒有啊。執行pip help,你可以看到pip freeze是輸出當前 Python 環境下已經安裝的所有第三方庫。因為我們創建此環境時開啟了選項--no-site-packages,意思就是在創建此虛擬環境中不從系統 Python 中把已經安裝了的庫也安裝到這里來,所以這里是一個干凈的新 Python 環境。你可以在這里調用pip或者easy_install來安裝各種你需要的庫到這個環境中來,而不會影響到你系統中 Python 的情況,所以說它是一個虛擬的Python 環境。
我們再回頭看下envtest目錄的結構,其下面的Scripts目錄中有python.exepip.exe這些程序,在虛擬環境已激活的情況下,你調用python或者pip都是調用的此目錄中的程序。此時系統中的python.exe被 virtualenv 通過設置環境變量隱藏了起來。而Lib目錄下就是存放各種新安裝的庫。
到這里你應該已經對 virtualenv 基本操作已經了解了,下面講些使用上的注意事項:
調用activate.bat開啟虛擬環境,你也可以用同目錄下的deactivate.bat來退出該虛擬環境。
建立虛擬環境時的參數--python=C:\Python26\python.exe是用來指定你想使用 Python 程序位置,所以你可以建立多個虛擬環境來指向多個 Python 版本。另外你要注意的是如果你在系統上安裝了多個版本的 Python, 你最先安裝的一個版本會被當做主要版本,你在命令行下打python時,調用的就是最先安裝的一個版本。其實這個是按照 PATH 中設定的路徑位置來確定的,你最好把你需要主要使用的版本相關路徑放在 PATH 環境變量中最前面。比如我的機器上,就是把C:\Python26和C:\Python26\Scripts作為 PATH 最前面兩個。這樣應該就能讓保證你主要版本的正常使用。
當你在一個虛擬環境下工作時,假如你想在當前環境下來執行一個 Python 程序,這時你在命令行下必須執行python foo.py,這樣 foo.py 才會在你當前已經激活的 virtualenv 下執行。作為比較如果你直接執行foo.py那么它仍然時在系統環境下執行的。
另外,Linux 下可以使用virtualenvwrapper來進行方便的管理和切換各個環境,可惜的是這東西在 Windows 下用不了。但幸好有一個簡單的腳本envdotpy來幫助你使用。把env.py放到 PATH 上的目錄內,譬如C:\Python26\Scripts下。之后先打開里面的DEFAULT_DIR_PATH變量,把它改成你集中存放 virtualenv 的地方,在我們上面的例子中就可以把這行改為:
DEFAULT_DIR_PATH = "C:\\envs\\"
之后你就不需要專門 cd 到這個目錄,而可以在任意路徑上通過env.py來進行激活,切換,退出 virtualenv 了。例如執行:env.py envtest就能激活envtest。執行env.py -q就能退出任意一個 virtualenv。
Winpdb 進行可視化調試
如果你使用的PyDev的話那么用其自帶的斷點調試應該就可以了。Winpdb則是為用其他簡單編輯器進行 Python 開發的用戶提供一個熟悉的調試環境。Winpdb不出意料的也在 Python(x, y)當中。所以如果裝上 Python(x, y) 你可以不斷發掘里面附帶的優秀工具。使用方法很簡單,假設程序名為 foo.py,那么在命令行中輸入:
winpdb foo.py
之后會彈出窗口,也就是一個大家都熟悉的 debug 圖形界面。需要注意的是這里需要點擊想要設置斷點的行,點擊 F9 設置斷點,然后該行底色會變為紅色,如下圖所示。
編碼問題
作為中文用戶,初學 Python 最容易碰到的問題估計就是編碼問題了。明明英文的都可以用到中文的時候就要出問題,而且出錯信息難以理解,想要解決問題又不知道從何開始。幸運的是編碼問題通過預防性的措施是很好避免的。下面從幾個方面來講講 Python 中處理中文及 Unicode 容易碰到的問題。
Unicode 編碼基礎
這里非常簡單的講一下編碼知識,此部分表述可能不太準確,如果你對 Unicode 更為了解的話請聯系我幫忙糾正。
你可以想象 Unicode 是一個很大的表,里面有著世界上所有的文字的個體,如英文中的字母,中文的漢字。事實上 Unicode 標準中每一個字都有一個唯一對應的編號,好比說 '中'字 對應十六進制 0x4E2D,而字母 'a' 對應的是十六進制 0x0061。這個編號是由Unicode Consortium這個組織來確定的。 如果說用這個編碼來對應字符來用于表示字符,理論上是可以的,這樣的話就是每一個數字編號能對應一個字符。
而實際情況中,不是每篇文章都用得到世界上所有的字符。譬如一篇英文文章就只有英文字母加上一些符號,用 Unicode 來進行存儲的話每個字符要浪費太多的空間。所以就有各種類型的編碼產生。編碼我們這里可以理解就是將一部分的 Unicode (比如說所有的中文,或者所有的日文)字符,以某種方式確定另外一個符號來代表他。中文常用編碼有 UTF8 和 GBK,仍然以 '中'字 為例, UTF8 編碼將對應 '中'字 的 Unicode 編號0x4E2D拆成三個的編號的組合,[0xE4, 0xB8, 0xAD],只有這幾個連在一起的時候才會被作為一個 '中'字 顯示出來;作為對比,GBK 編碼將 '中'字 對應的 Unicode 編號0x4E2D編碼成為兩個編號的組合[0xD6, 0xD0],在 GBK 編碼環境下只有這兩個編號一起時,才會顯示為 '中'字。
上面的例子中,如果把 UTF8 編碼后的[0xE4, 0xB8, 0xAD]放到 GBK 環境下來顯示會怎樣?這幾個編號跟 '中'字 在 GBK 下的編碼[0xD6, 0xD0],不同,則顯然不會顯示為 '中'字。這三個字符會跟排在其前后的字符一起,按照 GBK 的編碼規則找有沒有對應的字符。結果有可能顯示出一個毫不相關的字符,有時候為符號或者干脆不顯示,這種情況就算產生了亂碼。
Python 2.x 中的 String 與 Unicode
在 Python 2.x 中是有兩種字串符相關類型的,分別為 String 和 Unicode,兩者提供的接口非常類似,有時候又能自動轉換,蠻容易誤導人的。在 Python 3 中 這兩個類型分別用 Bytes 和 String 替代了。這個名字更能說明兩者的本質:Python 2.x 中的String 中存儲的是沒有編碼信息的字節序列,也就是說 String 中存儲的是已經編碼過后的序列,但他并不知道自身是用的哪種編碼。相反的Unicode 中存儲的是記載了編碼的字串信息,其中存儲的就是相應字符的 Unicode 編號。在這里用程序來說明,我們建立一個簡單的腳本名字為encoding.py,代碼如下:
#!/usr/bin/python
# -*- coding: utf-8 -*-
strs = "這是中文"
unis = "這也是中文".decode("utf8")
print strs[0:2]
print unis[0:2].encode('gbk')
print len(strs)
print len(unis)
前面兩行后面會解釋到,就是限定運行環境以及該腳本文件的編碼格式。此腳本在這里可以下載,如果你要自己寫的話請務必確保腳本的編碼是 utf8 而不是別的。在 Windows 下的運行結果在這里,我覺得正好能說明問題:
C:\SHARED\Dev\scripts>encoding.py
榪
這也
12
5
這里需要說明,我們的程序是 UTF8 編碼,主要意義是該程序中的所有直接寫出來的字串符(用"", ''括起來的字串符)是運用 UTF8 格式編碼的;然而 Windows 下的命令行是 GBK 環境。這里strs是一個String。事實上在 Python 2.x 中直接寫在程序中的字串符,其類型都是String(這里不考慮 string literal)。我們先直接輸出strs[0:2],得到的是一個亂碼字符(這個字符只是碰巧湊成是一個字)。如上面說的,String中存儲的是沒有編碼信息的字串序列,這里就是將strs中前兩個編號取出并嘗試顯示。由于命令行環境為 GBK 編碼,這里對應的字碰巧湊成了一個字,但是跟原本的字沒有任何關系。
unis是由一個String調用decode()方法得到,這正是在 Python 2.x 中取得Unicode的最基本的方式。由于String并不知道它本身是由什么編碼格式來進行的編碼,這里是我們的責任來確定他原來是用哪種編碼方式進行編碼。我們知道代碼中的編碼格式是 UTF8,所以我們可以用調用String的decode()方法來進行反編碼,也就是解碼, 把字串符從某種編碼后的格式轉換為其唯一對應的 Unicode 編號。unis為解碼獲得的結果,其在 Python 2.x 中對應類型就是Unicode,其中存儲的就是 每個字符對應的 Unicode 編號。
我們嘗試輸出unis的前兩個字符,在這里我們調用了Unicode的encode()方法。這就是編碼的過程。我們知道 Windows 命令行下的編碼是 GBK,只有采用 GBK 編碼的字符才能正確顯示。所以在這里我們通過調用Unicode的encode()方法,將unis中存儲的 Unicode 編號 按照 GBK 的規則來進行編碼,并輸出到屏幕上。這里我們看到這里正確的顯示了unis中的前兩個字符。要注意的是在命令行中直接printUnicode的話 Python 會自動根據當前環境進行編碼后再顯示,但這樣掩蓋了兩者的區別。建議總是手動調用encode和decode方法,這樣自己也會清楚一些。
后面兩者長度的差別也是佐證我們之前的例子。strs中存儲的是 UTF8 編碼后的編號序列,上面看到一個中文字符在 UTF8 編碼后變成三個連續的,所以strs長度為 3x4 = 12。你可以想象strs中存放的并不是中文,而是一系列沒有意義的比特序列;而unis中存儲的是對應的中文的 Unicode 編碼。我們知道每一個字符對應一個編號,所以五個字對應五個編號,長度為 5。
避免,和解決編碼產生的問題
了解了 Python Unicode 編碼解碼的這些概念后,我們來看看如何盡量的避免遇到讓人煩心的編碼問題。
首先如果你的代碼中有中文,那么一定要務必聲明代碼的編碼格式。根據PEP-0263中的介紹,在程序的最開始加上以下兩行注釋就能確定編碼:
#!/usr/bin/python
# -*- coding: utf-8 -*-
其中utf-8就是指定的編碼格式。事實上你應該總是使用 UTF8 作為你 Python 程序的編碼格式,因為未來的 Python 3 所有文件都將默認以 UTF8 編碼。另外除了聲明,你必須確定你用來編輯 Python 程序的編輯器是不是真的以 UTF8 編碼來存儲文件。
之后就是養成關于編碼解碼的好習慣。當你的程序有String作為輸入時,應該盡早的將其轉換為Unicode,再在程序中進行處理。再輸出的時候,也要盡可能玩,直到最后輸出的時刻才將Unicode編碼為所需編碼格式的String進行輸出。同樣的你必須保持你程序內部所有參與運算的字串都是Unicode格式。很多著名的 Python 庫例如django就是采用的這種方式,效果也蠻好。千萬不要依賴 Python 自己進行兩者之間的轉換,也不要將String和Unicode放在一起運算,這些行為一方面十分容易引起錯誤,另一方面在 Python 3 中已經無法再現。
雖說確定String的編碼格式是程序員的責任,但有時候你真的不知道有些字串符到底是什么編碼的。這里有一個神奇chardet能夠幫助你。以下是摘自其頁面上的例子,很好了說明了它的作用:讀入任意一串字符,猜測其編碼格式,并且給出猜測的確信度。
>>> import urllib
>>> urlread = lambda url: urllib.urlopen(url).read()
>>> import chardet
>>> chardet.detect(urlread("http://google.cn/"))
{'encoding': 'GB2312', 'confidence': 0.99}
>>> chardet.detect(urlread("http://yahoo.co.jp/"))
{'encoding': 'EUC-JP', 'confidence': 0.99}
>>> chardet.detect(urlread("http://amazon.co.jp/"))
{'encoding': 'SHIFT_JIS', 'confidence': 1}
>>> chardet.detect(urlread("http://pravda.ru/"))
{'encoding': 'windows-1251', 'confidence': 0.9355}
如果 confidence 非常低的話或者 chardet 直接報錯,多半是字串經過多次錯誤編碼解碼,要從別的地方找辦法解決問題。
在處理包含漢字的文本文件時,一個很常見的問題就是有時候會碰到帶有 UTFBOM的文件。這個簡單講就是文件頭幾個字節是用來表示文件是大端還是小端表示。在實際中用的很少,而且會帶來很頭疼的問題。有時候你確定你有一個文件是 UTF8 編碼的,但讀進來頭幾個字節就出錯,那么十有八九就是這個的問題。Python 在讀取文件時仍然是所有字節順序讀進來,不會透明的處理這個東西。所以要么你可以用編輯器來把文件另存為無 BOM 的,要么在 Python 中做處理。在標準庫中有codec里面提供了相關功能:
import codecs
s = f.read(3)
if s == codecs.BOM_UTF8:
print "BOM detected"
這樣可以簡單檢測 BOM 是否存在,剩下的部分就要你自己發揮了。
如果上面的介紹還不能讓你理解 Unicode 的概念,這里還有幾篇關于這個問題的文章:
介紹 Unicode 的兩篇文章1,2。關于 Unicode 有更為詳細的解釋。
Unicode In Python, Completely Demystified特別針對 Python 下的 Unicode 處理進行詳細的講解。
其他
除了上面幾個重要的問題之外,剩下的資源。
Vim Python開發 相關資源
事實上我現在自己是在用 Vim 寫 Python,感覺也蠻不錯。以下是相關資源。
UltimateVimPythonSetup比較新的一個專門針對 Python 的 Vim 配置文件。
Vim as Python IDE只要搜 Python 和 Vim 就一定會找到這一篇文章。
vimcolorschemetest所有的Vim 配色方案都在集結在這里。
Python 相關 Vim 插件
pythoncomplete.vim按上面的介紹配置一下,在自動輸入的時候按Ctrl-X, Ctrl-O就有很強力的自動補全了。
python.vim加強語法的高亮。
pyflakes.vim很棒的語法檢查,分析你的語法看避免低級錯誤。注意這個在Vim7.2下才有用, 如果是7.1則一點效果都沒有...
其他相關資源
這個把Python(x,y)里面所有的模塊基本上都講了一遍,我覺得外國人肯定都希望這個有個英文版的。
這個名字看起來像個Python庫(其實它還真的是一個...),但他總體來說其實是一份文檔, "Python每周一個模塊"。作者持續幾年每周介紹一個Python標準庫中的庫。你可以把他看做是一個Python標準庫文檔的一個很棒的補充,當你看標準庫中的介紹看的云里霧里的時候,不妨來這邊找找相應的介紹。因為這里的例子給的很全,而且基本上你用的到的偏門的庫這里都有介紹哦。另外一個好消息是PyMOTW有一份很棒中文翻譯版。
reddit.com/r/python和python.org planet
Python 相關的文章和資源。就我個人經歷來說,每次都能在這里看到很多有用的東西。