●大標●

Python 之旅 (二)

文/馬兒 (marr@linux.org.tw)

先聲明一下,看完「Python 之旅」並不會馬上讓每位朋友成為 Python 高手,這應該不是本專欄的用意,不過,引發讀者對 Python 的興趣,讓大家一窺 Python 的應用實況,卻是這一系列內容的企圖。「Python 之旅」主要定位為導讀的角色,希望在本專欄告一段落之際,讀者已具備應用 Python 的興趣及預備進階的實力,屆時配合其他文件或書籍,相信能夠達到快速吸收的效果。

行遠必自邇,接下來的內容,將按部就班地介紹幾種基本的資料型別、流程控制、語法結構、內建函式等,練功紮馬步的工夫是省不了的,僅管可能乏味些,但透過實例操作,並和作業系統環境結合,希望能儘量與日常應用搭配,讓「Python 心法」更容易地融入讀者的腦筋迴路。

文中的範例,原則上都在 Mandrake Linux 7.2 環境裡執行與測試,適用於其他版本的 GNU/Linux 應該並無問題,我將預設讀者對於 Linux 已經具備基本之認識,特別是 shell 環境的操作,以及檔案系統結構的認識,另外,C 語言的基本觀念與技巧,也會有助於 Python 的學習,比如說,事先看過 A Book on C (作者是 Al Kelley 與 Ira Pohl) 的內容。這些簡單的先備知識,可以成為多種程式語言學習背景支撐,建議讀者不妨抽空予以納入。

●中標●

從內建資料型別 numbers 談起

Python 的數值資料型別基本分為四類:

數值資料型別

範例

1. 整數 (Plain Integers)

7, -7, 256

2. 長整數 (Long Integers)

7L, 10L, -777777777777L

3. 浮點數 (Floating Point Numbers)

7.0, 2e8, -7e10

4. 複數 (Complex Numbers)

3+2j, -4-2j, 4.2+6.3j

Python 裡的整數型別,是以 C 語言的 long 型別來實作,也就是 32 bits 的精準度。直接依序看些操作範例:

example$ python

Python 1.5.2 (#1, Sep 30 2000, 18:08:36) [GCC 2.95.3 19991030 (prerelease)] on linux-i386

Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam

>>> x = 5 + 14 / 7 - 3 * 2

>>> x

1

>>> 5 / 2

2

>>> 5.0 / 2

2.5

>>> 2 ** 8

256

 可愛的四則運算。

 別驚慌,5 / 2 答案是 2,這是正常的,如果你想得到 2.5 這樣的答案,應用浮點數型別即可,只要搞清楚其運算的特性,Python 能夠跑出相當令人滿意的答案。

 ** 代表「指數運算」,所以此處指的就是 28

關於長整數所使用的 L 符號,事實上也可以用小寫的 l 符號,不過還是建議使用大寫的 L 符號,因為 1l 看起來實在太像 11 了。

>>> 10 ** 9

1000000000

>>> 10 ** 10

Traceback (innermost last):

File "<stdin>", line 1, in ?

OverflowError: integer pow()

>>> 10L ** 10

10000000000L

>>> x = 2147483647

>>> x

2147483647

>>> x + 1

Traceback (innermost last):

File "<stdin>", line 1, in ?

OverflowError: integer addition

 OverflowError 就是指該型別「數值滿溢」的錯誤,1010 對整數型別而言已吃不消。

 使用長整數後,其結果幾乎已經到達「予取予求」的地步,其有效範圍只跟記憶體容量有關。

 x86 PC 上,一般整數型別的最大極限為 231 - 1,即 214783647。

 一旦超過 214783647 後,即顯示整數型別的滿溢錯誤。

例如 10L ** 100000 可就讓我的 PIII CPU 忙上好一陣子了 (大約一分鐘餘)。有興趣的朋友,可以試著以 time 來計時玩玩看。

example$ cat power_test1.py

print 10L ** 10000

example$ cat power_test2.py

print 10L ** 100000

example$ time --output=pt1 python power_test1.py

example$ time --output=pt2 python power_test2.py

Python 裡的浮點數型別,是以 C 語言的 double 型別為基礎來實作,我們可以透過 Python 的長整數來模擬許多浮點數計算的技巧,這在要求位數精準的場合上很實用,不過原則上,直接應用浮點數是來得更具效率。

>>> 7.3 ** 2.4

118.025232408

>>> 3.5e8 * 2.0e7

7e+15

>>> x = 1.00000000001

>>> x

1.00000000001

>>> x = 1.000000000001

>>> x

1.0

 科學記號的運算。

 超過精準位數的話,就會被截掉,使用時要小心。

下列是複數的運算,天啊,喚起我國中時代的回憶。

>>> x = (3 + 2j) * (4 + 9j)

>>> x

(-6+35j)

>>> x.real

-6.0

>>> x.imag

35.0

 複數的指定方式,只需要在某個整數或浮點數之後,接個 j 或 J 符號即完成。

 分別呼叫 x 的「實部」與「虛部」數值,這是應用了「物件 x」的屬性 (attribute) 功能。

拿 Python 的直譯器當作計算機來用,也是非常方便。

>>> tax = 6.5 / 100

>>> price = 250

>>> price * tax

16.25

>>> price + _

266.25

 特別注意到上述的 _ (底線) 符號,它代表的是前一個計算值的結果。

還可以呼叫一些內建函式或載入函式模組。

>>> round(3.14159, 4)

3.1416

>>> abs(-3.000000001)

3.000000001

>>> import math

>>> math.sqrt(2)

1.41421356237

 round() 是內建函式,可傳回你想要的四捨五入精準位數值。

 abs() 也是內建函式,可傳回絕對值。

 import math 就是載入 math 函式模組,隨後便可呼叫其 sqrt() 開平方根功能。

以下的例子是載入 cmath 函式模組,以取得更多數值應用 (大抵用於處理 complex numbers) 的功能協助,其傳回值屬於複數型別。隨 Python 程式所安裝的系統檔案裡,就應該找得到一份 test_cmath.py 的測試範例。

example$ /usr/lib/python1.5/test/test_cmath.py

Calling sin(1.000000) = 0.841471

Calling log10(1.000000) = 0.000000

Calling acos(1.000000) = 0.000000

Calling asinh(1.000000) = 0.881374

Calling acosh(1.000000) = 0.000000

Calling exp(1.000000) = 2.718282

Calling tan(1.000000) = 1.557408

Calling cosh(1.000000) = 1.543081

Calling sqrt(1.000000) = 1.000000

Calling atanh(0.200000) = 0.202733

Calling sinh(1.000000) = 1.175201

Calling tanh(1.000000) = 0.761594

Calling atan(0.200000) = 0.197396

Calling log(1.000000) = 0.000000

Calling cos(1.000000) = 0.540302

Calling asin(1.000000) = 1.570796

PI = 3.14159265359

E = 2.71828182846

●BOX_BEGIN●

●標題●

數值資料型別的進階資訊

有關完整的 number 型別資料,已經被整理於 Python Library Reference [1],有興趣的朋友,歡迎前往挖寶。

另外,為了讓 Python 在幾何學與機械工程運算上能有更佳的表現,歐洲開發人員撰寫了專為科學計算場合應用的 ScientificPython 2.2 模組 [2],其他進階的科學運算技巧,諸如矩陣、行列式、向量運算、多項式運算、線性規劃、3D 繪圖模組等,都可以在此擴充模組中獲得。對於教學人員或是研究人員,應該是相當值得參考的資源。

●BOX_END●

●中標●

流程控制 (Control Flow)

Python 的流程控制,主要由條件式 (conditionals)、迴圈 (loops),以及例外 (exceptions) 所構成,此處的條件式和迴圈,與其他程式語言學習的邏輯觀念並無二致,縱使具有語法技巧上的小差異,其趣味性實在不高,我便不再贅述,讀者手邊若有第 16 期的Linuxer 雜誌,可逕自翻閱參照「Python 簡介」一文的說明。接下來,我將直接以範例程式,帶領讀者認識 Python 流程控制的功能。

下列是一個簡單的複利計算範例程式,程式執行後會輸出計算結果。

example$ cat print_pr.py

principal = 1000

rate = 0.05

num_years = 5

year = 1

while year <= num_years :

principal = principal * (1 + rate)

print year, principal

year = year + 1

 值得注意之處,在於 Python 的流程控制語法裡使用了 : (冒號) 符號,以及大量的縮排效果 (indentation and block-structuring),這樣的設計哲學可能跟 C、Perl 等語法相異其趣。

 如果有人對於輸出格式不夠滿意的話,可以採用 print "%3d %0.4f" % (year, principal) 敘述式,則可以得到新的輸出結果:

1 1050.0000

2 1102.5000

3 1157.6250

4 1215.5063

5 1276.2816

也就是說,Python 的格式化字串,其處理方式和 C 語言裡的 printf() 函式相仿,

舉例來說,"%3d" 就可以將一個整數欄位,進行寬度為 3 的靠右對齊處理,而 "0.4f" 則可以將一個浮點數設定為四位小數的顯示格式。

 

配合前述的基礎,我們已經可以撰寫個簡單的 I/O 測試程式囉。

example$ cat high_low.py

number = 78

guess = 0

while guess != number :

guess = input(Guess a number: )

if guess > number :

print Too high. Try again.

elif guess < number :

print Too low. Try again.

Print Just right.

 

example$ python high_low.py

Guess a number: 100

Too high. Try again.

Guess a number: 50

Too low. Try again.

Guess a number: 78

Just right.

下列的範例程式,可以產生每行顯示 16 個字元的 ASCII 表:

example$ cat ascii.py

#!/usr/bin/python

i = 0

while i < 256 :

print chr(i),

if i != 0 and i % 16 == 0 :

print

i = i + 1

 chr() 是 Python 的函式,讀進一個 0 至 256 之間的整數後,會傳回其對應之 ASCII 字元。注意到 chr(i) 後面所接的 , (逗號) 符號,它表示資料以緊接一個空白字元的方式輸出。

 i % 16 是為了控制每一行輸出顯示的字元數量。

圖:ascii.py 執行後會顯示 ASCII 表。

●中標●

序數資料 (sequence) 的操作

所謂序數資料型別,就是幾個有序物件的集合,並可以自然數為其索引。Python 的序數型別包含有字串 (string)、串列 (list)、值組 (tuple)。分別介紹如下:

在 Python 當中,只要將幾個文字包含在單引號、雙引號、三引號裡,就可以建立一個字串:

>>> a = 'hello world'

>>> b = "It's nice to learn Python"

>>> c = """

... this is a triple quote demo.

... see? we can go even cross lines :)

... remember to end it with another triple quote.

... """

>>> a[0]

'h'

>>> b[5:18]

'nice to learn'

>>> c[-22:]

'another triple quote.\012'

 分別示範的是單引號、雙引號、三引號之字串建立方式。

 字串的索引運算可以取得當中的元素 (element),字串 a 裡的元素,是由 0 開始標序,所以 a[0] 是 h,而 a[1] 是 e。

 字串的分割運算可以取得元素組。

 注意到最後面的 \012,指的是換行符號。

字串某種角度來看,像是「以字元為元素的串列」,因此,有關索引運算,以及分割運算,我會在即將介紹的串列部份中,更加詳細地說明。

●BOX_BEGIN●

●標題●

原始字串 (raw string)

在某一個字串前面加上 r 或 R 字元,可以建立一個原始字串,例如 rhello world 或 R\the Python user 等,原始字串經常被使用於 Python 的正規表示式 (regular expression) 場合上,尤其在處理脫逸字元時,是一項實用的特殊用法。

●BOX_END●

串列 (list) 是以中括符號為設定格式,先讓我們仔細觀察其操作特性:

 

>>> x = [1, 2.0, 7, "eleven"]

>>> x[1]

2.0

>>> x[-2]

7

 串列裡可以包含多種資料型別,例如數值、字串等,甚至也可以再包含另一個串列在裡頭。例子中的 x 就是一個包含四個元素 (element) 的串列。

 串列 x 裡的元素,同樣是由 0 開始標序。

 以 -2 來表示從串列的後頭往回標序,所以 x[-2] 是倒數第二個元素資料值。

我們可以使用分割運算 (slice),來截取串列項目裡的某段子集,由於這項操作技巧太實用了,建議讀者不但要搞懂,最好還能熟練。

>>> y = ["first", "second", "third", "fourth"]

>>> y[1:-1]

['second', 'third']

>>> y[:3]

['first', 'second', 'third']

>>> y[-2:]

['third', 'fourth']

>>> y[-2:-3]

[]

 

以串列 y 為例,其完整的索引 (index) 設定狀況分析如下:

 

y =

[

 

"first"

,

"second"

,

"third"

,

"fourth"

 

]

   

 

 

 

 

 

正向索引

 

0

 

1

 

2

 

3

 

4

 

負向索引

 

-4

 

-3

 

-2

 

-1

     

 原則上,分割運算是採用 [m:n] 格式,m 為起點,n 為終點,不管是利用正向索引或是負向索引皆可。搭配圖示對照後,可以很輕易地看出 y[1:-1] 指的是 secondthird 所組成的子串列,也就稱為串列 y 的一個「切片」(slice)。

 分割運算若省略「指明的」起點,則代表從最前面的元素取起。

 分割運算若省略「指明的」終點,則代表取至最後面的元素為止。

 當起點落在終點之後時,分割運算的結果會是空串列。

最後,我們介紹的是值組 (tuple) 型別,乍看之下,它與串列的運作方式類似,不同處在於值組是以小括號符號為設定格式,串列是以中括號為設定格式,而且值組是不可變的物件,串列是可變的物件。

>>> t = ("this", "is", "a", 6, "element", "tuple")

>>> len(t)

6

>>> max(t)

'tuple'

>>> min(t)

6

 值組建立的方式與串列相似,同樣可以包含多種型別的元素。

 使用內建運算元 len() 取得值組 t 的長度大小。

 使用內建運算元 max() 與 min() 取得值組 t 的最大值與最小值。

另外,序數型別既然包括字串、串列、值組三種,在操作應用時,三者是具有共通之運算元的,比如之前已介紹過的索引運算、分割運算,以及 len()、min()、max() 等。

●中標●

物件的可變與不可變

什麼是「可變的」物件呢? 允許使用者對物件進行新增、修改、刪除等動作,該物件則稱為「可變的」物件。事實上,每一種儲存在 Python 程式裡的資料,都是以物件的方式存在,而每一個物件的組成,則包括有識別字、資料型別,以及物件的值。當一個物件建立完畢之後,它的識別字和型別都不可以再更改,但它裡面的值卻有可能被修改,而可以被修改者,就是可變物件 (mutable),否則稱為不可變物件 (immutable)。

>>> a = 256

>>> b = 'Python'

>>> print id(a), id(b)

134957540 134969832

>>> a is b

0

>>> print type(a), type(b)

<type 'int'> <type 'string'>

 使用內建函式 id() 可以取得物件的識別字資訊,不過,大致只能得到一長串不甚具備意義的數字,藉以了解 a 與 b 「確實」是兩個不同的物件。

 使用 is 運算元可以直接比較兩個物件的識別字是否相同,由於我們知道 a 與 b 的識別字確實不同,因此運算結果是 0。

 使用內建函式 type() 可以取得物件的型別資訊。

>>> y = [first, second, third, fourth]

>>> id(y)

135309760

>>> y[2] = '3rd'

>>> y.append('5th')

>>> y

['first', 'second', '3rd', 'fourth', '5th']

>>> id(y)

135309760

>>> y.sort()

>>> y

['3rd', '5th', 'first', 'fourth', 'second']

>>> id(y)

135309760

 以索引運算來指定想要變更的資料值。

 以物件方法 append() 來增加 '5th' 字串到串列 y 的最後面。

 以物件方法 sort() 來將串列元素排序。

 特別注意到,經過上述的操作及修改過程,串列 y 的識別字仍維持相同,表示串列 y 是可變物件。

●中標●

內建資料型別的概況整理

 

Python 提供將近二十餘種不同的內建型別,我們目前幾乎只見識到其「冰山一角」,由基本的內建型別出發,將有助於認識所有的資料型別,對於尚未介紹到的資料型別,則希望有興趣的讀者能再持續深入研究。Python 是座寶山,最好是有興趣同行,如此才容易逛出心得。

型別大類

型別名稱

說明

None

NoneType

空物件

Numbers

IntType

LongType

FloatType

ComplexType

整數

長整數

浮點數

複數

Sequences

StringType

ListType

TupleType

XRangeType

字元字串

串列

值組

由 xrange(i,j,k) 函式傳回值

Mapping

DictType

辭典集

Callable

BuiltinFunctionType

BuiltinMethodType

ClassType

FunctionType

InstanceType

MethodType

UnboundMethodType

內建函式

內建方法

類別物件 *

使用者定義函式

類別物件之實例變數 *

物件方法

物件方法

Modules

ModuleType

模組

Classes

ClassType

類別定義 *

Class Instance

InstanceType

類別實例變數 *

Files

FileType

檔案

Internal

CodeType

FrameType

TracebackType

SliceType

EllipsisType

編譯位元碼

執行框架

例外追蹤堆疊

由延伸分割所產生的型別

由延伸分割所使用的型別

表:內建之 Python 資料型別

* 值得注意的是,ClassType 和 InstanceType 同時分屬於兩類中。

善用 Python 內建之資料型別及物件模型,在專案開發上,是一個相當重要的技術議題,Python 長期以來的改版發展,一大課題便在改善各式物件及資料型別的效率。不過,實務上的做法,在要求效率的場合中,會建議直接以 C 或 C++ 來撰寫程式,然後再匯出成為 Python 的延伸模組。

Integer

12 bytes

Long Ingeger

12 bytes + (nbits/16 + 1) * 2 bytes

Float

16 bytes

Complex

24 bytes

List

16 bytes + 4 bytes * #_elements

Tuple

16 bytes + 4 bytes * #_elements

String

20 bytes + 1 byte * #_characters

Dictionary

24 bytes + 12 * 2n bytes, n = log2(nitems) + 1

Class Instance

16 bytes + dictionary objects

Xrange

24 bytes

表:內建資料型別於 32 位元電腦上的記憶體需求狀況

●中標●

坐擁寶山貴在自知

在 Python 的原始程式碼裡,通常就會附隨相當完整的文件,極富參考價值。只看附隨的文件就足以自學成師,具備此番功力者,應該不是尋常人,不過,不假外求,卻是學習歷程中值得努力的一種境界,這往往代表著一個人獨立思考求解的邏輯能力。為了讓用功的讀者早日認識及習慣 Linux 環境下的自學方式,在此簡介 Python 文件的製作及閱讀方式,相信對於其他的自學機會也能派上用場。

example$ cd /usr/share/doc/python-docs-1.5.2/Doc

此目錄裡存放了許多 .tex 檔案,其實也就是 LaTeX 格式的檔案,已經認識 LaTeX 的朋友,至此應該就能會心一笑,自個兒高興地編譯出 postscript 檔案,而初次遇著 LaTeX 的朋友也無須納悶,

由 .tex 檔案製作出 .ps 檔案的步驟,通常並不難。

 

example$ make

example$ ls *.ps

api.ps ext.ps lib.ps tut.ps

在一陣 TeX 編譯過程之後,一切順利的話,會看到上述四個 .ps 檔案,即 Postscript 格式的檔案,可以用於預視或直接送交雷射印表機處理。如果你遇到困難的話,八成是 LaTeX 相關套件安裝不完備,先擱著也無所謂。

●BOX_BEGIN●

●標題●

入門 Python 工具書提示

1. Python 技術參考辭典

David Beazley、Guido Van Rossum 著

張裕益 譯

書中第四章內容,即整理了各式資料型別的運算元及表示式,能夠給讀者完整的概念及資訊。而整本書也可以視作精簡的自學手冊,提供平常交叉參考之用。書的尺寸刻意濃縮,用意良善,不過,其排版方式不夠明確,不容易讓人快速尋得所需的資訊。

2. Teach Yourself Python in 24 Hours

Ivan Van Laningham 著

淺顯白話的敘述方式,可以當作入門的教科書,裡頭的範例略顯冗長,主要是訴求新手的直覺式學習,整本書啃起來像是軟麵包一般,易於下嚥。書中的第三部份,專注於介紹 Python GUI 的內容,堪稱一大特色,而第一部份則可做為本文的補充教材,適合有興趣的讀者進一步釐清基本概念。

3. The Quick Python Book

Daryl Harms、Kenneth McDonald 著

解說清晰是本書的特色,我非常喜歡書中的排版方式,加上內容廣度相當大,讓人覺得是一本物超所值的好書。透過諸如 JPython 與 Zope 的介紹,讀者可以見識到 Python 學習後的應用實況。

4. Learning Python

Mark Lutz、David Ascher 著

早期 Python 的入門書籍還不若今日眾多,Learning Python 很長一般時間成為 Python 使用者唯一的正式選擇。這本書直接介紹 Python 核心而重要的部份,大抵都是設計概念層面的內容,依我個人的經驗所示,花大約兩個星期內的時間略讀過它,效益上應會最佳。

●BOX_END●

cd 在 X Window 環境下,可以執行 gv (GhostView) 程式,用以預覽 .ps 檔案內容,如果設定好印表機,執行列印動作後,一份美觀大方的文件便輸出完成。

example$ gv tut.ps

如果有人對英文說明文件感到頭痛,那麼我建議仍試著「略讀」,特別是配合本 Python 專欄的系列介紹後,英文 Python 文件理應不再存有過大的鴻溝。

不過,可惜的是,我未能成功將 *.tex 檔案轉換成 html 檔案格式,途中遭遇到一些狀況,似乎是 latex2html 程式的問題。

只好請讀者們自行前往 http://www.python.org/doc/ 網頁,

線上閱讀 HTML 版本的文件內容。值得注意的是,網站上所謂的 Current Documentation 通常指的是 Python 2.1 版文件,閱讀時請配合自己手邊使用之 Python 程式版本。

Tutorial

深入淺出的教學手冊。

Library Reference

詳細的函式解說。

Language Reference

Python 語法解說。

Extending and Embedding

說明如何將 Python 程式與其他程式語言進行擴充與結合。

Python/C API

Python 的 C 程式底層介面說明。

Macintosh Reference

專供麥金塔使用者的參考手冊。

如果你懶得製作上述的文件,那麼上網同樣可以下載所有的檔案,你可以在 http://www.python.org/doc/current/download.html 網頁裡,下載自己需要的部份:

HTML

html-2.1.zip (2008 Kb)

PDF (A4)

pdf-a4-2.1.zip (3621 Kb)

PostScript (A4)

postscript-a4-2.1.zip (1958 Kb)

LaTeX

latex-2.1.zip (1000 Kb)

值得一提的是,Python Tutorial 已有同好譯成中文 [4],翻譯品質極高,歡迎大家多多捧場,並給予譯者一些鼓勵。

●BOX_BEGIN●

●標題●

Python 2.1 問世

截稿之際,適逢 Python 2.1 正式版在網路上誕生,順道在此公告週知。有興趣的朋友可以由 http://www.python.org/ftp/python/2.1/ 下載,Linux 版本原始碼壓縮檔約 4.1 MB,而 Windows 版本安裝檔案約 6.0 MB,完整安裝約需 20 MB 硬碟空間。

從今年初開始進入 alpha 測試階段的 Python 2.1,終於如期在四月間發表正式版,象徵 Python Software Foundation 已成功輔助 Python 的發展。程式語言本身並沒有重大的差異,但在安裝親和度與伴隨之文件更新方面,已更上一層樓。Windows 環境下的使用者,下載新版程式後,可以嘗試直接瀏覽新版的線上文件。

●BOX_END●

 

●中標●

Python 網路資源

[1] 最新版 Python Library Reference 線上文件

http://www.python.org/doc/current/lib/lib.html

[2] ScientificPython 網頁

http://starship.python.net/crew/hinsen/scientific.html

[3] Python, Random Numbers, and Monte Carlo Simulation

http://www.linuxdev.net/features/articles/ldpymc_1.shtml

[4] 中文 Python 教學文件,譯者署名為 周譯樂

http://coder.9ug.com/language/script/python/tut_tw/tut.html

[5] Non-Programmers Tutorial For Python

http://www.honors.montana.edu/~jjc/easytut/easytut/easytut.html

[6] Python HOWTO Documents

http://py-howto.sourceforge.net/