Python 程式語言

教學目標:

認識 Python 語言特性,熟悉基本類別使用技巧,了解類別庫等物件模組運作及應用方式。

教學內容:

Introduction: 特色與應用場合。

Basic Types: 數值、字串、串列、辭典集

Structure: 流程控制、函式、模組。

Useful Modules: 系統常用之模組工具。

Object: 物件結構及類別庫。

Demo: Python程式範例。

 

 

A language that makes it hard to write elegant code makes it hard to write good code.

-- Eric S. Raymond

特色

延革

可參考物件導向式語言之簡介,網址http://catalog.com/softinfo/objects.html

Zope程式人員開發手冊http://www.zope.org/Documentation/ZDG

 

通信論壇管理程式 mailman 網站 http://www.list.org/

http://www.tuxedo.org/~esr/cml2/index.html

學習 Python 之相關先備知識

物件觀念,shell 環境的操作,以及檔案系統結構,都是重要的先備知識,另外,C 語言的基本觀念與技巧,也會有助於 Python 的學習,比如說,事先看過 A Book on C (作者是 Al Kelley Ira Pohl) 的內容。這些簡單的先備知識,可以成為多種程式語言學習背景支撐,建議不妨抽空予以納入。

線上資源

程式語言的學習方法,其原則與日常語言極其相似。

「應用於日常生活」及「依樣畫葫蘆」,應是兩條學習捷徑。

在生活裡尋找程式語言的應用機會,能讓語言學習增添動機與樂趣,模仿前人的程式範例,可省茫然摸索的時間浪費,進而積累舉一反三之效。

星艦 (Starship Python) 網站裡的艦員名單裡,高手如雲,通常他們也樂於分享自己的學習經驗,不妨抽空前往取經。http://starship.python.net/crew.html

安裝 Python

下載網址

http://downloads.activestate.com/ActivePython/windows/2.1/ActivePython-2.1.1.msi

進入交談式環境

Linux: $ python

Windows: python (command line)

ActivePython: Python Win Environment

離開交談式環境

送出 EOF 字元

Linux: Ctrl + D

Windows: Ctrl + Z

ActivePython: File - Exit

計算機功能

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

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

>>> round(3.14159, 4)

3.1416

>>> abs(-3.000000001)

3.000000001

>>> import math

>>> math.sqrt(2)

1.41421356237

基本型別

字串 (string)、串列 (list)、值組 (tuple) 屬於 Python 的序數型別 (sequence)。

辭典集 (dictionary) 屬於 Python 的映射物件 (mapping object)。

example$ python

Python 2.2.1 (#1, Aug 30 2001, 17:36:05) [GCC 2.96 20000731 (Mandrake Linux 8.1 2.96-0.61mdk)] on linux-i386

Type "copyright", "credits" or "license" for more information.

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

>>> x

1

>>> 5 / 2 u

2

>>> 5.0 / 2

2.5

>>> 2 ** 8 v

256

>>> 10 ** 9

1000000000

>>> 10 ** 10 u

Traceback (innermost last):

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

OverflowError: integer pow()

>>> 10L ** 10 v

10000000000L

>>> x = 2147483647 w

>>> x

2147483647

>>> x + 1 x

Traceback (innermost last):

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

OverflowError: integer addition

>>> 7.3 ** 2.4

118.025232408

>>> 3.5e8 * 2.0e7 u

7e+15

>>> x = 1.00000000001

>>> x

1.00000000001

>>> x = 1.000000000001 v

>>> x

1.0

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

>>> x

(-6+35j)

>>> x.real v

-6.0

>>> x.imag v

35.0

流程控制

example$ cat print_pr.py

principal = 1000

rate = 0.05

num_years = 5

year = 1

while year <= num_years : u

principal = principal * (1 + rate)

print year, principal v

year = year + 1

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

1 1050.0000

2 1102.5000

3 1157.6250

4 1215.5063

5 1276.2816

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$ cat ascii.py

#!/usr/bin/python

i = 0

while i < 128 :

print chr(i), u

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

print

i = i + 1

物件型別比較

字串 (string)、串列 (list)、值組 (tuple) 屬於 Python 的序數型別 (sequence)。

辭典集 (dictionary) 屬於 Python 的映射物件 (mapping object)。

在 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'

串列 (list) 是以中括符號為設定格式,下列是其操作特性:

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

>>> x[1]

2.0

>>> x[-2]

7

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

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

>>> y[1:-1] u

['second', 'third']

>>> y[:3] v

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

>>> y[-2:] w

['third', 'fourth']

>>> y[-2:-3] x

[]

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

y =

[

 

"first"

,

"second"

,

"third"

,

"fourth"

 

]

   

 

 

 

 

 

正向索引

 

0

 

1

 

2

 

3

 

4

 

負向索引

 

-4

 

-3

 

-2

 

-1

     

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

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

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

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

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

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

>>> len(t) v

6

>>> max(t) w

'tuple'

>>> min(t) x

6

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

 

物件的可變與不可變

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

>>> a = 256

>>> b = 'Python'

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

134957540 134969832

>>> a is b v

0

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

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

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

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

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

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

>>> id(y)

135309760

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

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

>>> y

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

>>> id(y) x

135309760

>>> y.sort() w

>>> y

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

>>> id(y) x

135309760

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

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

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

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

連續數值的指定: 內建函式 range()

range 是一個便利的內建函式,可以傳回一組整數值的串列。

>>> range(7) Œ

[0, 1, 2, 3, 4, 5, 6]

>>> (mon, tue, wed, thu, fri, sat, sun) = range(7) 

>>> mon 

0

>>> sun 

6

>>> range(-5, 5) Ž

[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4]

>>> range(0, 10, 3) 

[0, 3, 6, 9]

下列的簡單範例,可以將一組資料進行編號。

>>> weekday = ('mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun')

>>> for day in range(len(weekday)):

... print day, weekday[day]

...

0 mon

1 tue

2 wed

3 thu

4 fri

5 sat

6 sun

實務程式寫作上,range函式 for 迴圈經常搭配。還記得之前的 ascii.py 範例嗎? 我們可以使用 for 迴圈與 range 函式加以改寫,來個溫故知新。

example$ cat ascii.py

#!/usr/bin/python

for i in range(128) :

print chr(i),

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

print

相較之下,程式碼本身顯得簡化了,但語法型態與 while 不同,讀者應用時大抵挑自己習慣者即可。

 

 

 

 

 

語法結構

Python 識別字指定規則:

http://www.python.org/doc/2.1/ref/identifiers.html

Python 所有保留字資訊:

http://www.python.org/doc/2.1/ref/keywords.html

 

函式指定方式

以保留字 def 為首,並依照縮排規則撰寫內容。

慣例上,函式的第二行會是一段「三引號註釋字串」。

文件字串 (documentation string)

""" 所括夾的文字,稱為「文件字串」。

可以透過如 fact.__doc___ 物件方法,來顯示文件字串內容。

>>> def fact(n):

... """Return the factorial of the given number."""

... r = 1

... while n > 0:

... r = r * n

... n = n - 1

... return r

...

>>> fact.__doc__

'Return the factorial of the given number.'

>>> fact(5)

120

>>>

fact() 是一個階乘函式範例,請注意到最後一行的保留字 return,如果少了 return 敘述,則預設會以 None 值傳回。以範例函式 fact() 來看,n 是函式的參數 (argument),其傳回值 r n 的階乘結果。

>>> def fact(n=10):

... """Return the factorial of the given number, with the defult input value."""

... r = 1

... while n > 0:

... r = r * n

... n = n - 1

... return r

...

>>> fact(5)

120

>>> fact()

3628800

>>>

上述程式片段示範了函式預設輸入值的設定方式,試試以 fact(5) 呼叫範例函式,會得到傳回值 120,而以 fact() 呼叫,則會傳回以 10 為預設輸入值的 3628800

>>> def power(x, y=2):

... r = 1

... while y > 0:

... r = r * x

... y = y - 1

... return r

...

>>> power(2, 4)

16

>>> power(3)

9

>>> power()

Traceback (innermost list):

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

TypeError: not enough arguments: expected 1, got 0

>>> power(2, 4, 3)

Traceback (innermost list):

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

TypeError: too many arguments: expected 2, got 3

>>>

一個函式可以設定多個輸入值,上例 power() 函式可以接受兩個輸入值,但至少需要 (expect) 一個輸入值,因為參數 y 有個預設值 2,輸入參數時可以省略之。以 power(2, 4) 呼叫時,會成功傳回 16,以 power(3) 呼叫時,會自動以 power(3, 2) 為輸入值,傳回 9。如果以 power() 呼叫,則會產生 TypeError 的錯誤訊息,它會明確告知「參數不足」,你必須至少輸入幾個參數,如果是以 power(2, 4, 3) 來呼叫,則會得到「參數過多」的 TypeError 訊息。

>>> power(y=4, x=2)

16

>>>

另一個有用的應用技巧,稱為「關鍵字派定法」(keyword passing),是以類似 power(x=2, y=4) 方式來呼叫,明確地將參數值配合變數名稱通知函式,甚至也可以用 power(y=4, x=2) 方式來呼叫,如此一來,兩者的呼叫結果是完全相同,意即其參數值順序可以任意改變。

我們接著來看一些任意個輸入值的例子,相信其實用價值極高。

>>> def maximum(*numbers):

... if len(numbers) == 0:

... return(None)

... else:

... max = numbers[0]

... for n in numbers[1:]:

... if n > max:

... max = n

... return max

...

>>> maximum(3, 8, 5)

8

>>> maximum(2, -5, 9, 1, 7)

9

函式 maximum() 的用意很明顯,我們可以輸入任意個數的數值,而它最後會傳回最大值。例如 maximum(3, 8, 5) 會傳回 8,而 maximum(2, -5, 9, 1, 7) 會傳回 9。值得注意的地方,就是其處理不定參數個數的技巧,參數指定以 *numbers 方式代表,而 numbers 本身是一個值組 ( tuple,而非 list)

下列的例子,將參數設定的可能狀況大抵做了整合介紹,我們可以一窺函式參數派定的相關細節,值得讀者反覆測試觀察。

>>> def args_func(a, b, c=9, *other1, **other2): Œ

... return [a, b, c, other1, other2.items()] 

...

>>> args_func(1, 2, 3) Ž

[1, 2, 3, (), []]

>>> args_func(b=1, a=2) 

[2, 1, 9, (), []]

>>> args_func(1, 2, 3, 4, 5, 6, 7, 8, 9) 

[1, 2, 3, (4, 5, 6, 7, 8, 9), []]

>>> args_func(1, c=3, b=2, d=4, e=5)

[1, 2, 3, (), [('d', 4), ('e', 5)]]

>>> args_func(d=4, e=5)

Traceback (innermost last):

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

TypeError: not enough arguments; expected 2, got 0

Œ 範例函式 args_func() 可以輸入三個 (以上的) 參數,參數名稱分別是 abc,其中 c 有預設值為 9a b 是必要的輸入參數。而 *other1 用以指定 abc 關鍵字之外的參數值 (不定個數)**other2 則是用以指定 abc 關鍵字之外的派定值 (同樣是不定個數)

 函式 args_func() 非常簡潔,直接把所有的輸入參數值以串列資料型別傳回。其中的 other1 屬於值組資料型別,而 other2 則是辭典集 (dictionary) 資料型別。別慌,稍後會為讀者解說辭典集的相關細節。

Ž 給定三個參數值,它們會分別成為函式 args_func() abc 的設定值,此時 other1 other2 都是空空如也。

 使用關鍵字派定法來指定 a b 的參數值,而使用 c 的預設參數值。

 給定了九個參數值,前三個依序成為 abc 的參數值,後六個數值則成為值組 other1 的元素內容。

給定了五個參數值,第一個成為 a 的參數值,b c 以關鍵字派定法來指定,而最後的 d e 則成為不定個數的關鍵字派定值,它們被辭典集 other2 所收留了。

內建資料型別 dictionary 的使用

辭典集是 Python 裡的內建映射物件 (mapping object),也就是由一個物件集合來作為另一個物件集合的鍵值索引。映射物件和之前談過的序數資料相較,在概念上有擴充、補強的涵意,善用映射物件的特性,可以協助我們將看似複雜的問題,以相當直覺的方式解決。兩者具有若干不同之處,例如序數以索引運算作為其取值方式,映射物件的元素成份來源很有彈性,映射物件是不做排序的,而且映射物件是可變物件。

在功能上,辭典集的實作相當於資料結構裡的雜湊表 (hash table) 或是相關陣列 (associative array),所以你會看到「鍵-值」(key-value pairs)的表示法。通常我們會以字串來做為辭典集的 key,因為有意義的字串可以帶來「望文生義」的效果,不過,一定要以不可變物件來做辭典集的 key,而 value 的部份就全無限制了。

>>> x = [] Œ

>>> y = {} Œ

>>> x[0] = 'Beatles' Œ

Traceback (innermost last):

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

IndexError: list assignment index out of range

>>> y[0] = 'John Lennon' 

>>> y[1] = 'Paul McCartney' 

>>> y[2] = 'George Harrison' 

>>> y[3] = 'Ringo Starr' 

>>> y[0] + " and Yoko Ono" Ž

'John Lennon and Yoko Ono'

>>> y 

{3: 'Ringo Starr', 2: 'George Harrison', 1: 'Paul McCartney', 0: 'John Lennon'}

>>> y.keys() 

[3, 2, 1, 0]

>>> y.values() 

['Ringo Starr', 'George Harrison', 'Paul McCartney', 'John Lennon']

>>> y.items() 

[(3, 'Ringo Starr'), (2, 'George Harrison'), (1, 'Paul McCartney'), (0, 'John Lennon')]

>>>

 注意到辭典集的儲存,並沒有特定的順序方式,如果想要依特定的排序方法處理資料,可以另尋變通方法。

>>> Beatles = {'leader':'John','bass':'Paul','guitar':'George','drum':'Pete'} Œ

>>> Hurricanes = {'drum':'Ringo'} 

>>> Beatles.update(Hurricanes) Ž

>>> Beatles

{'drum': 'Ringo', 'leader': 'John', 'bass': 'Paul', 'guitar': 'George'}

>>> Beatles.get('leader', "Not available") 

'John'

>>> Beatles.get('manager', "Not available") 

'Not available'

>>>

Ž 透過 update() 物件方法,我們更新了 Beatles 鼓手的設定資料。

 get() 物件方法是詢問 Beatles 裡是否有 leader 此一鍵值,若存在則傳回其對應之元素值,否則會傳回後頭的字串資料。

: 辭典集的方法和操作

項目

說明

len(dict)

傳回辭典集 dict 裡的元素個數。

dict[k]

傳回鍵值 k 的元素內容。

dict[k]=v

dict[k] 的內容設定為 v

del dict[k]

dict[k] 元素項目移除。

dict.clear()

將辭典集 dict 所有元素項目全部移除。

dict.copy()

將辭典集 dict 整個複製。

dict.has_key[k]

如果辭典集 dict 含有鍵值 k 的話,則傳回 1,否則傳回 0

dict.items()

以值組 (key, value) 的串列型別傳回辭典集中所有元素。

dict.keys()

傳回辭典集 dict 的所有鍵值。

dict.values()

傳回辭典集 dict 的所有元素值。

dict.update(other)

將辭典集 other 所有物件更新至辭典集 dict 當中。

dict.get(k [, other])

如果 dict[k] 存在則傳回 dict[k],否則傳回 other

 

模組的使用

Linux Mandrake 8.1 為例,其安裝目錄為 /usr/lib/python2.1。目錄裡包含類似 string.pyos.pyfind.py 的檔案,我們可以透過 import string, os, find 之類的程式語法呼叫這些模組內容。也可以直接閱讀這些 .py 檔案的程式碼,相信部份檔案的內容,對你而言已不再全是天書。

實際動手撰寫自己的模組之前,我們得先認識內建函式 dir() 的功能,它可以將許多物件內部的資訊顯示出來。

>>> dir() Œ

['__builtins__', '__doc__', '__name__']

>>> dir(__doc__) 

[]

>>> print __doc__ 

None

>>> print __name__ Ž

__main__

>>> type(__builtins__)

<type 'module'>

>>> dir(__builtins__) 

['ArithmeticError', 'AssertionError', 'AttributeError', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'FloatingPointError', 'IOError', 'ImportError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'None', 'NotImplementedError', 'OSError', 'OverflowError', 'RuntimeError', 'StandardError', 'SyntaxError', 'SystemError', 'SystemExit', 'TypeError', 'ValueError', 'ZeroDivisionError', '_', '__debug__', '__doc__', '__import__', '__name__', 'abs', 'apply', 'buffer', 'callable', 'chr', 'cmp', 'coerce', 'compile', 'complex', 'delattr', 'dir', 'divmod', 'eval', 'execfile', 'exit', 'filter', 'float', 'getattr', 'globals', 'hasattr', 'hash', 'hex', 'id', 'input', 'int', 'intern', 'isinstance', 'issubclass', 'len', 'list', 'locals', 'long', 'map', 'max', 'min', 'oct', 'open', 'ord', 'pow', 'quit', 'range', 'raw_input', 'reduce', 'reload', 'repr', 'round', 'setattr', 'slice', 'str', 'tuple', 'type', 'vars', 'xrange']

>>> print __builtins__.__doc__ 

Built-in functions, exceptions, and other objects.

Noteworthy: None is the `nil' object; Ellipsis represents `...' in slices.

>>>

Œ 當我們身處新啟動之 Python 交談環境裡,輸入 dir() 可以顯示 local symbol table的名稱串列,共計三個。

 進一步提供 __doc__ dir() 做為參數,傳回空串列,表示 __doc__ 物件已無相關屬性 (attributes)print __doc__ 顯示其為 None 物件。

Ž __name__ 是一個字串物件,表示目前執行「程式」的名稱,其值為 __main__

 __builtins__ 則是一個模組物件,持續以 dir(__builtins__) 觀察,可以顯示模組 __builtins__ 的全部屬性。

 顯示 __builtins__ __doc__ 屬性內容。

注意到,每個正在執行的主要程式,其程式名稱 ( __name__ 屬性) 會是 __main__,如果是以模組型態被 import 進來,那麼該模組程式便會以原本檔案名稱為 __name__ 的值。請觀察下列程式範例的說明,兩個極其簡化的「土製模組」。

example$ cat other_mod.py

#!/usr/bin/python

print "this is from other_mod."

print __name__

example$ chmod 755 other_mod.py; ./other_mod.py

this is from other_mod.

__main__

example$ cat my_mod.py

#!/usr/bin/python

"""Simple module example for illustrating how __*__ works."""

import other_mod

print "this is from my_mod."

print __name__

example$ chmod 755 my_mod.py; ./my_mod.py

this is from other_mod.

other_mod

this is from my_mod.

__main__

import 的模組檔案,其內容會被執行,所以範例 my_mod.py 在執行之後,會先讀進 other_mod.py 的程式片段,接著才是 my_mod.py 的程式片段。請特別留意 __name__ 屬性值的變化,這項控制技巧經常被使用。

Python 實際搜尋模組路徑的設定值,可以由 sys 模組裡的 path 變數值取得。

>>> import sys

>>> sys.path

['', '/usr/lib/python1.5/', '/usr/lib/python1.5/plat-linux-i386', '/usr/lib/python1.5/lib-tk', '/usr/lib/python1.5/lib-dynload', '/usr/lib/python1.5/site-packages', '/usr/lib/python1.5/site-packages/PIL']

>>> import marr

Traceback (innermost last):

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

ImportError: No module named marr

>>>

以載入 foo 模組為例,其實際尋找模組的過程順序如下:

1. 是否存在名為 foo 的目錄,並且裡頭含有該模組的檔案。

2. 是否存在 foo.sofoomodule.sofoomodule.sl 或是 foomodule.dll

3. 是否存在 foo.pyo

4. 是否存在 foo.pyc

5. 是否存在 foo.py

以一個 .py Python 原始碼檔案而言,經過編譯後,會產生一個名為 .pyc bytecode執行檔,當尋找某個模組名稱時,要是 .py 檔案的日期不比 .pyc 檔案來得新,Python 直譯器會直接將編譯好的 .pyc 檔案載入,若是 .py 檔案的日期比 .pyc 檔案來得新,通常就表示 .py 檔案內容已更新,Python 直譯器會重新編譯之,以產生新的 .pyc 檔案,然後才進入載入動作。而 .pyo 檔案只有在直譯器以 -O 選項啟動之後才會產生,這類檔案裡的資訊通常比 .pyc 檔案來得多,包含有原始程式的行號以及除錯資訊,因此 .pyo 檔案的載入速度會較慢,但程式的執行速度會較快。

.pyc 或是 .pyo 檔案的編譯動作,是在程式裡頭呼叫 import 後才會發生,對 Python語言來說,模組檔案不止是設計概念的切割,它更從強化模組執行效率的角度,鼓勵程式員善用模組檔案功能。如果自製的模組檔案越來越多,其應用自然越顯重要,此時便要認真為自製模組找個適當的存放路徑,比較常見的方式之一,是設定相關的環境變數值,例如變數 PYTHONPATH

: Python 相關環境變數設定

變數名稱

說明

PYTHONDEBUG

python -d 啟動模式相同。可產生 Python 的語法解析除錯資訊。

PYTHONHOME

與模組搜尋路徑設定相關的變數。

PYTHONINSPECT

python -i 啟動模式相同。以交談模式來執行 Python 程式。

PYTHONOPTIMIZE

python -O 啟動模式相同。以最佳化模執行 Python 程式。

PYTHONPATH

增加模組搜尋路徑。

PYTHONSTARTUP

交談模式就緒前所執行的程式路徑。

PYTHONUNBUFFERED

python -u 啟動模式相同。記錄未做緩衝的二元標準輸出輸入。

PYTHONVERBOSE

python -v 啟動模式相同。執行過程詳列相關處理資訊。

請將現行目錄設定在 my_mod.py 檔案存放的目錄,然後進入 Python 的交談環境。

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

>>> dir()

['__builtins__', '__doc__', '__name__']

>>> import my_mod

this is from other_mod.

other_mod

this is from my_mod file.

my_mod

>>> dir()

['__builtins__', '__doc__', '__name__', 'my_mod']

>>> dir(my_mod)

['__builtins__', '__doc__', '__file__', '__name__']

>>> print my_mod.__doc__

Simple module example for illustrating how __*__ works.

>>> print my_mod.__file__

my_mod.py

>>>

除了基本的資料型別之外,Python 裡的每一個模組物件都擁有自己的名稱空間。下列是一個範例程式,可以協助列出模組物件的名稱資訊。

example$ cat namespace.py

#!/usr/bin/python

import sys

k = sys.modules.keys()

print "Keys:", k

print "-" * 30

for i in k:

if i == "__main__":

print ">>>", i, "__dict__", sys.modules[i].__dict__

print "-" * 30

print dir()

在實務設計上,Python 程式員會運用模組的方式,將物件做適當的切割,切割好的程式可以被包在一個 Python 類別庫 (package) 裡,以便進一步有效管理大型的軟體專案。有了函式、模組、類別庫等一系列的架構,我們便可更直覺地管理軟體專案的物件體系。

: 名稱空間階層示意

其他有用的函式

為了方便接續的內容,我們先來認識一個實用的模組,稱為 string,顧名思義,可用於協助處理字串物件。

>>> import string

>>> date = "Fri May 18 CST 2001"

>>> piece1 = string.split(date)

>>> piece1

['Fri', 'May', '18', 'CST', '2001']

>>> time = "12:03:27"

>>> piece2 = string.split(time, ':')

>>> piece2

['12', '03', '27']

>>> string.digits

'0123456789'

上述範例,讓我們見識到模組 string 裡有個 split() 的物件方法,可以將一個字串變數值,依空白字元 (預設狀況) 為切割點,分解成數個小字串,形成一個字串串列傳回。如果切割條件不是空白字元時,在 split() 所接參數中予以指定,如 split(time, ':') 就是指定要以 ':' 字元為切割點。最後則是顯示模組 string 有個字串變數 digits,內容設定為 '0123456789'

如果我們想把上述字串串列裡的「數字」,如 '18' '2001',由字串型別轉換成數值型別,可以怎麼做呢? 下列是個方法。

>>> def try_ai(s):

... if s[0] in string.digits:

... return string.atoi(s)

... else:

... return s

...

>>> import string

>>> date = "Fri May 18 CST 2001"

>>> piece = string.split(date)

>>> finish_ai = map(try_ai, piece)

>>> print finish_ai

['Fri', 'May', 18, 'CST', 2001]

>>>

首先,定義一個叫做 try_ai() 的函式,它在讀進字串後,會比對字串的第一個字元,如果第一個字元是屬於阿拉伯數字,那麼就會嘗試將字串轉換成整數,最後傳回其整數型別資料。是的,你會發現它的演算規則有些天真,不過,我們暫時還不需要一個無懈可擊的轉換函式。

接著,我們載入模組 string 之後,利用內建函式 map() 將自製函式 try_ai 與字串串列 piece 連結起來,如此一來,便能如願將 piece 裡的部份字串,轉換成數值型別。顯然 map() 函式在此例中幫上大忙,簡潔地協助我們將自製函式與序列資料做了巧妙結合。

接下來,我們就可以進一步稍微改良原本天真的 try_ai() 函式。

>>> def try_ai(s):

... if ':' in s:

... ts = string.split(s, ':')

... return map(string.atoi,ts)

... if s[0] in string.digits:

... return string.atoi(s)

... else:

... return s

...

>>> import string

>>> date = "Fri May 18 12:03:27 CST 2001"

>>> piece = string.split(date)

>>> finish_ai = map(try_ai, piece)

>>> print finish_ai

['Fri', 'May', 18, [12, 3, 27], 'CST', 2001]

>>>

>>> piece = ['Fri', 'May', '18', '12:03:24', 'CST', '2001']

>>> def strp(x, y):

... return x + ' ' + y

...

>>> r = reduce(strp, piece)

>>> r

'Fri May 18 12:03:24 CST 2001'

>>>

上述程式片段,處理效果剛好與之前的程式相反,它會把字串串列重組成一個長字串。重點就是利用了內建函式 reduce(),其運作方式同樣要輸入一個函式名稱及一個序數資料,不過,目的是要把序數資料的元素「合併減少」成一個。reduce() 也可以應用在數值串列上,以下便是這樣的範例。

>>> n = range(1, 11)

>>> def mult(x, y):

... return x * y

...

>>> f = reduce(mult, n)

>>> f

3628800

>>>

說穿了,它還是一個階乘的範例,每呼叫一次 mult() 函式,數值串列的個數會越來越少,最後傳回一個階乘結果,在此例中,即 10! 的答案。

下列是一個簡化版本的閏年判斷程式,我們將介紹另一個函式 filter()

>>> def leap(y):

... if (y%400) == 0:

... return 1

... elif (y%100) == 0:

... return 0

... elif (y%4) == 0:

... return 1

... return 0

...

>>> n = range(1900, 2001)

>>> leap_year = filter(leap, n)

>>> leap_year

[1904, 1908, 1912, 1916, 1920, 1924, 1928, 1932, 1936, 1940, 1944, 1948, 1952, 1956, 1960, 1964, 1968, 1972, 1976, 1980, 1984, 1988, 1992, 1996, 2000]

函式 filter() 同樣是接一個函式名稱與一個序數資料為參數,重點在於,在自製函式中,你必須把「想留下來的資料」,其函式傳回值設為 1 (代表 true),而把「不想留下來的資料」,其函式傳回值設為 0 (代表 false)。如此一來,filter() 函式在依序處理完序數資料後,還是會傳回一個序數資料,而且應該只留下你想要的資料。

lambda 表示式

lambda 表示式是個實用而重要的工具,其功能及本質還是函式,差別在於 lambda 沒有「明確的函式名稱」,而且通常只用於簡潔的函式敘述。

>>> n = range(1, 11)

>>> f = reduce(lambda x, y: x * y, n)

>>> f

3628800

>>>

又是熟悉的階乘函式,有趣的是,它的表示方式非常簡潔,乍看之下,初學者可能以為是天書了。lambda 表示式的語法如下:

lambda 參數串列: 表示式

例如 x + yx * ys[9] 都是表示式的例子。早期,lambda 表示式的概念是取自 Lisp 語言,使用上有其便利及優勢,不過,對初學者而言,使用 lambda 表示式通常還是得花時間熟悉,若是「畫虎不成反類犬」,搞到程式大亂就得不償失了。

 

 

 

存取檔案系統資訊

>>> import os Œ

>>> os.getcwd() 

'/home/marr'

>>> os.listdir(os.curdir) Ž

['.bash_history', '.bash_logout', '.bash_profile', '.bashrc', '.muttrc', 'signature', '.ssh', '.viminfo', '.vimrc']

>>> os.chdir('/tmp') 

>>> os.path.join('usr', 'local', 'bin') 

'usr/local/bin'

>>> mount_point = '/mnt/cdrom'

>>> rpm_dir = 'Mandrake/RPMS'

>>> complete_dir = os.path.join(mount_point, rpm_dir) 

>>> print complete_dir

'/mnt/cdrom/Mandrake/RPMS'

>>> os.path.split(complete_dir)

('/mnt/cdrom/Mandrake', 'RPMS')

>>> os.path.basename(os.path.join(complete_dir, 'some.rpm'))

'some.rpm'

>>> os.path.dirname(os.path.join(complete_dir, 'some.rpm'))

'/mnt/cdrom/Mandrake/RPMS'

系統檔案處理的必備工具

下列的常數與系統環境有關,使用上無須輸入值。

常數名稱

功能說明

傳回值範例

os.name

傳回「作業系統環境名稱」字串

'nt', 'posix'

sys.platform

傳回「作業平台名稱」字串

'win32', 'linux-i386'

os.environ

傳回「系統環境變數」辭典集

{'HOME': '/home/marr', 'SHELL': '/bin/bash'}

os.curdir

代表「現行工作目錄」

'.'

os.pardir

代表「父層目錄」

'..'

既然 os.environ 傳回的是辭典集,我們還可以透過 os.environ["PATH"] 方式來取出特定的環境變數值。另外像 sys.argv 是經常被利用到的變數,可以參考下列的例子:

example$ cat prn_argv.py

#!/usr/bin/python

import sys

if len(sys.argv) < 2:

print "enter some arguments with this script."

sys.exit(0)

else:

print "there are %d arguments: " % (len(sys.argv),)

for n in range(len(sys.argv)):

print sys.argv[n],

example$ ./prn_argv.py

enter some arguments with this script.

example$ ./prn_argv.py argu1 argu2 argu3

there are 4 arguments:

./prn_argv.py argu1 argu2 argu3

下列的函式與檔案資訊有關,使用上必須輸入字串變數 (通常就是檔案名稱)

函式名稱

函式功能

操作範例

os.path.exists()

測試字串變數是否存在。

os.path.exists('/etc/passwd')

os.path.isdir()

測試字串變數是否為目錄。

os.path.isdir('/etc')

os.path.isfile()

測試字串變數是否為檔案。

os.path.isfile('/etc/X11/X')

os.path.islink()

測試字串變數是否為連結檔案。

os.path.islink('/etc/X11/X')

os.path.samefile()

測試兩個字串變數是否指向同一檔案。

os.path.samefile('/bin/sh', '/bin/bash')

os.path.isabs()

測試字串變數是否為絕對路徑。

os.path.isabs('/home/marr')

os.path.getsize()

傳回字串變數之檔案大小。

os.path.getsize('/etc/passwd')

os.path.getatime()

傳回字串變數之最後存取時間。

os.path.getatime('/etc/passwd')

os.path.getmtime()

傳回字串變數之最後修改時間。

os.path.getmtime('/etc/passwd')

下列的函式與檔案處理有關,使用上必須輸入字串變數 (通常包含檔案名稱)

函式名稱

函式功能

操作範例

os.mkdir()

以指定模式建立一個新目錄。

os.mkdir("/tmp/beatles", 0700)

os.rmdir()

刪除指定目錄。

os.rmdir("/tmp/beatles")

os.rename()

將檔案或目錄名稱更改。

os.rename("/tmp/beatles", "/tmp/smiths")

os.link()

為檔案建立一個連結。

os.link("/tmp/old_file", "/tmp/ln_file")

os.symlink()

為檔案建立一個符號連結。

os.symlink("/tmp/old_file", "/tmp/sln_file")

os.unlink()

刪除檔案的連結。

os.unlink("/tmp/sln_file")

os.stat()

取得檔案完整相關資訊。

os.stat("/etc/X11/X")

os.lstat()

stat() 相同,但不傳回連結的檔案。

os.lstat("/etc/X11/X")

 

 

 

 

 

 

物件的使用

class Circle: Œ

pass Œ

myCircle = Circle() 

myCircle.radius = 5 Ž

print 2 * 3.14 * myCircle.radius

 

Œ 定義一個 Circle 類別,其內容「空空如也」。

 建立一個新個體,名為 myCircle

Ž 設定 myCircle radius 值為 5。其設定方式,是在個體物件名稱後加上 . 符號,成為 myCircle.radius 這樣的描述方式。

個體物件的設定值,可由 __init__ 這個起始函式自動完成,通常 __init__ 又被稱為類別的「建構子」(constructor)。每次有新的個體物件被建立,都會找尋 __init__ 的設定內容,自動完成新個體物件的起始設定值。

 

class Circle: Œ

def __init__(self): Œ

self.radius = 1 Œ

def area(self): 

return self.radius * self.radius * 3.14159 

myCircle = Circle() 

print 2 * 3.14 * myCircle.radius 

myCircle.radius = 5 Ž

print 2 * 3.14 * myCircle.radius Ž

print myCircle.area()

 

Œ 類別 Circle 裡有個 __init__() 函式,設定其 radius 值為 1。注意到 self 這個參數,慣例上它就是 __init__() 函式的第一個參數,當 __init__ 起始設定時,self 就會被設定為新建立的個體上。

 新個體物件建立後,有自己的個體變數 (instance variables),如 radius 即為一例。

Ž 個體變數的值可以重新設定。

 可以為個體物件設定物件方法 (method),其方式就像定義函式一般。

class Circle:

def __init__(self, r=1):

self.radius = r

def area(self):

return self.radius * self.radius * 3.14159

c = Circle(5)

print c.area()

 

上述則是另一個改進版本的 Circle 類別內容設定,增加預設變數 r,並指定其預設值為 1。如此一來,我們便可透過 Circle(5) 這樣的方式來指定新個體的建立。

 

物件繼承 (inheritance) 範例

類別庫

一旦模組 (module) 的運作模式熟悉了,則類別庫 (package) 便不難理解,因為類別庫只是在一個目錄之下,將一堆功能相近而能相互引用的程式碼檔案收集在一起。而呼叫類別庫時,則是以其上層目錄名稱來命名。

example$ cat mathproj/__init__.py

print "Hello from mathproj init"

__all__ = ['comp']

version = 1.03

example$ cat mathproj/comp/__init__.py

__all__ = ['c1']

print "Hello from mathproj.comp init"

example$ cat mathproj/comp/c1.py

x = 1.00

example$ cat mathproj/comp/numeric/__init__.py

print "Hello from nemeric init"

example$ cat mathproj/comp/numeric/n1.py

from mathproj import version

from mathproj.comp import c1

from n2 import h

def g():

print "version is", version

print h()

example$ cat mathproj/comp/numeric/n2.py

def h():

print "Called function h in module n2"

 

執行來測試上述的類別庫程式內容時,必須確認 mathproj 目錄位於 Python 的搜尋路徑中,最簡單的方式,便是使用者的現行目錄設定在 mathproj 目錄之上,然後再開始一個 Python 的對話環境。

>>> import mathproj

Hello from mathproj init

>>> mathproj.version

1.03

>>> mathproj.comp.numeric.n1 Œ

Traceback (innermost last):

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

AttributeError: comp

>>> import mathproj.comp.numeric.n1

Hello from mathproj.comp init

Hello from nemeric init

>>> mathproj.comp.numeric.n1.g()

version is 1.03

Called function h in module n2

None

產生密碼檔內容

http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/59873

如果你需要隨機產生密碼檔的輸入值,本程式可以依照「密碼長度」、「是否含有數字」兩項條件來輸出結果。

計算目錄容量大小

http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/86554

類似 Unix du 工具程式,可用來計算 Windows 系統裡目錄所佔的空間大小,指令選項包括 km

動態更改 Python 系統路徑

http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52662

傳回值為三類:

-1 新增失敗,目錄不存在

1 新增成功

0 新增失敗,路徑已存在