在第一堂下課之前,來談談 練習 2:哈囉!世界! 做了什麼?為了方便,把範例程式碼再貼過來一下:
# coding=UTF-8
filename = raw_input('檔名:')
f = open(filename, 'r')
b_str = f.read()
f.close()
print b_str.decode('utf-8') # 這是什麼?
print b_str.decode('utf-8').encode('utf-8') # 這是什麼?
Python 的 Unicode 支援
第 1 行是 編碼宣告(Encoding declaration),這是個魔法註解(Magic comment),它告訴 Python 直譯器,這個原始碼檔案是以 UTF-8 來編碼,如果不這麼加上這個魔法註解,Python 2.x 會以 ASCII 編碼來解讀原始碼,那麼就會出現以下錯誤:SyntaxError: Non-ASCII character '\xe6' in file hello.py on line 1,
but no encoding declared; see http://www.python.org/peps/pep-0263.html for details
魔法註解中設定了 UTF-8,這是因為在 Ubuntu 12.04 LTS 中,預設採用的文字編碼是 UTF-8。有趣的是,有時我上課會問學員一個問題:「你用的原始碼文字編碼是什麼?」很多學員答不出來,很多人不知道自己作業系統中開個純文字檔編碼是什麼,不知道在整合開發環境(Integrated Development Environment, IDE)中開個原始碼編碼是什麼,當然也就不知道為什麼把 A 專案的原始碼放到 B 專案中程式碼會出現亂碼。
如果你連 UTF-8 是什麼都不知道,那建議你看看我寫的 亂碼 1/2 中這幾篇文件: 在 Python 2.x,程式中所有文字都是
str
的實例,可以視為代表文字資料的位元組序列(Byte sequence)。例如在 Python 中,可以使用 len
函式來取得序列長度,不過以下的程式會顯示 6,即使 '測試'
是兩個字元:
# coding=UTF-8
text = '測試'
print len(text) # 顯示 6
這是因為
'測試'
這兩個字元,使用 UTF-8 編碼的話,會使用六個位元組,len
函式實際上是計算位元組序列的長度,而不是字元長度。 在 Python 2.x 中,如果想要用 Unicode 來代表文字,也就是想要用
unicode
型態來封裝文字,可以使用 Unicode 字面常量(Unicode literal) 來表示,也就是在文字前置一個 u
符號。例如:
# coding=UTF-8
text = u'測試'
print type(text) # 顯示 "<type 'unicode'>"
print len(text) # 顯示 2
直譯器執行程式時,會使用
unicode
實例來代表文字資料,這點可以從 type
函式得知,它用來探知實例的型態,使用 len
取得一個 unicode
實例的長度時,它會告訴你有幾個字元。 除了使用 Unicode 字面常量來建立
unicode
實例外,在 Python 2.x 中,如果有個 str
的實例,可以使用其 decode
方法,指定 str
的實例代表何種編碼的位元組序列,這樣就可以傳回文字的 unicode
實例。相反地,如果使用 unicode
實例的 encode
方法,可指定編碼取得文字實際編碼後的位元組序列。 在 Python 3.x 中,預設原始碼檔案必須是 UTF-8 編碼。如果原始碼檔案想要是 UTF-8 以外的編碼,同樣必須在第 1 行放置編碼宣告。Python 3.x 中,文字是
str
型態的實例,不過 str
代表的是 Unicode(而不是像 Python 2.x 代表的是位元組序列),下面這個程式在 Python 3.x 中執行的話:
text = '測試'
print(type(text)) # 顯示 "<class 'str'>"
print(len(text)) # 顯示 2
len
表示會有兩個字元。如果想取得文字實際編碼後的位元組序列,可以使用 encode
方法指定編碼,這會傳回一個 bytes
實例,如果有個 bytes
實例,可以使用 decode
指定編碼,傳回代表 Unicode 的 str
實例。例如下圖是在 Python 3.x 互動交談環境中的測試實例: 如果想知道更多 Python 中有關文字編碼的細節,可以再參考 Python 的編碼 這篇文件。
基本 I/O
接下來看看有關基本 I/O 的部份,你可以使用open
函式來開啟檔案,開啟時指定存取模式,'r'
表示讀取,'w'
表示寫入,open
函式會傳回 file
實例,使用 read
方法可以讀取檔案內容,以 str
型態傳回,如先前談到,傳回的 str
實際上代表著位元組序列。 以下是個實際的讀取程式範例:
import sys
file = open(sys.argv[1], 'r')
content = file.read()
print content
file.close()
程式第一行匯入(import)了
sys
模組,sys.argv
是個 list
,其中儲存了執行程式時的命令列引數(Command line arguments),索引 0 固定都是執行時的模組名稱,而後是跟隨著的引數,例如執行 python hello.py one two three
時,sys.argv[0]
就會是 'hello.py'
,其餘索引則是 'one'
、'two'
、'three'
。print
在 Python 2.x 中是個陳述句,用來顯示指定的資料,不使用檔案時,記得使用 close
關閉檔案。 類似地,一個寫入檔案的程式範例如下,
write
方法會將文字的位元組序列寫入至檔案中:
import sys
file = open(sys.argv[1], 'w')
file.write('test')
file.close()
如果要逐行讀取檔案呢?可以使用
file
的 readline
方法,例如逐行讀取一個文字檔案的所有內容,可以在 while
迴圈中進行:
import sys
file = open(sys.argv[1], 'r')
while True:
line = file.readline()
if not line: break
print line
file.close()
如果讀不到東西了,那
readline
會傳回 ''
,在 if
判斷式中,''
會被視為 False
。 while
後加上 :
表示區塊開始,Python 中使用縮排來決定區塊範圍。注意!你可以自行決定縮排字元,但是 Python 中縮排要一致,如果縮排想使用 Tab 字元,那所有原始碼就都要使用 Tab 字元縮排,如果要使用四個空白字元,那所有原始碼就得是四個空白字元來縮排,強制統一縮排,是 Python 的特色,也是 Python 的文化。 可以使用
file
的 readlines
方法一次讀取所有檔案內容,這會傳回 list
,每個索引處代表一行內容,一個程式範例是這樣的:
import sys
file = open(sys.argv[1], 'r')
for line in file.readlines():
print line
file.close()
for in
語法可作用在 list
上,逐一取出 list
中的元素並指定給變數,對於 for line in file.readlines()
是這樣閱讀的:對於 file.readlines()
傳回的 list
中每個元素,將之指定給 line
。 實際上對於 Python 來說,讀取檔案最好的方式,就是不要去
read
它,這是什麼意思?是這樣的…
import sys
for line in open(sys.argv[1], 'r'):
print line
這是 Python 的風格,也是 Python 的文化,這樣的寫法好處就是增加了可讀性,你不用自行使用
close
關閉檔案,for in
結束後自行關閉檔案file
實例被回收後,檔案就會關閉,for line in open(sys.argv[1], 'r')
是這樣閱讀的:對於開啟檔案的每一行。除了可讀性外,這個語法還能讓讀取更有效率,不過現階段你不用管這些事,只要當這語法背後施了些魔法就好。
整合開發工具
真正開發程式時,你也許會想要使用整合開發工具,在 Python 的世界中,我確實花了些時間去瞭解有沒有主流的整合開發工具,不過事後覺得,在 Python 的世界中,除了團隊因素決定之外,更多時候是個人口味問題,基於課程時間,我就不逐一討論我調查過的那些工具特色為何了,以下列出一些課程前調查過的幾個:- PyCharm / IntelliJ IDEA
- PyDev / Eclipse plugin
- Komodo IDE
- Spyder
- WingIDE
- NINJA-IDE
- Python Tools for Visual Studio
參考資源
- 直譯器的選擇與安裝
- Distutils、Distribute 與 Pip 間的關係
- 哈囉!世界!