前言 - 碁峰資訊epaper.gotop.com.tw/pdfsample/a443.pdf · 式來逐一查看它們。...
TRANSCRIPT
前言
本書要介紹 Python程式語言,主要的對象是初學者,不過,就算你已經會寫程式,且想要再學習 Python,這本書也適合你。
本書的節奏不快,會從最基本的開始,一步步帶領你進入更複雜且更多樣的主題。我會
結合食譜與教程的風格來解釋新的名詞與概念,但不會一次說明太多主題。這本書會很
快進入真正的 Python程式,並頻繁地使用它。
雖然本書的目的,是為了介紹這個語言,但我也會加入一些較進階的主題,例如 NoSQL資料庫與訊息傳遞程式庫。之所以選擇它們,是因為它們解決某些問題的能力比標準的
方法優秀。之後你要下載並安裝 Python外部套件,當 Python內建的功能不適合你的應用程式時,瞭解它們會有很大的幫助,且嘗試新事物也是件有趣的事情。
我也會介紹一些不能做的案例,特別是當你曾經寫過其他的語言,想要將那些寫法應用
在 Python上時。我不會假裝 Python是個完美的語言,所以會教你如何避免錯誤。
如果有些東西比較難以理解,或有更適合的 Python 風格的做法,我會像
這樣加入一些提示。
對象
本書適合想要學習逐漸成為世界最受歡迎的程式語言的人,無論你是否曾經學過程式
設計。
vi | 前言
大綱
本書的前七章會解釋 Python的基礎知識,你應該依序閱讀這幾章。之後的章節會說明如何在特定的領域中使用 Python,例如 Web、資料庫、網路等,你可以依你喜歡的順序閱讀。前三個附錄會展示 Python在藝術、商務與科學上的應用。如果你尚未安裝Python 3,接下來你會看到如何安裝它。之後是每章練習題的解答,最後有一些實用的備忘錄。
第一章
程式就像編襪子或烤馬鈴薯的指令。這一章會用一些實際的 Python程式來展示這個語言的外貌、能力,以及在現實世界的用途。Python足以媲美其他的語言,但還是有一些不盡完善的地方。舊版的 Python(Python 2)正逐漸讓位給新版本(Python 3),如果你用的是Python 2的話,請在電腦上安裝 Python 3。請使用互動式解譯器來實驗本書的範例。
第二章
這一章會展示 Python最簡單的資料類型:布林、整數、浮點數,與文字字串。你也會學到基本的數學與文字操作。
第三章
這一章介紹 Python更高層級的內建資料結構:串列、tuple、字典與集合。你可以像樂高積木一樣,用它們來建構更複雜的結構。你會學到如何使用迭代器(iterator)與生成
式來逐一查看它們。
第四章
在第四章,你會結合程式結構與之前幾章提到的資料結構,來做比較、選擇或複製。你
會看到將程式包裝成函式的方法,並且學會使用例外來處理錯誤。
第五章
本章會展示如何將程式擴充為更大型的程式結構:模組、套件與程式。你會在這裡看
到:程式與資料的放置處、進出傳遞資料、處理選項、瀏覽 Python Standard Library,並看看除了它們之外的選項。
第六章
如果你曾經使用其他語言編寫物件導向程式,Python會讓你比較輕鬆。第六章會解釋何時該使用物件與類別,以及何時該使用模組或串列與字典。
前言 | vii
第七章
學習像專家一樣管理資料。本章都在討論文字與二進位資料,也就是 Unicode字元與 I/O可為你帶來的樂趣。
第八章
資料都必須被送往別的地方。在本章中,你會從基本的一般檔案、字典,與檔案系統開
始。接下來,你會看到如何處理常見的檔案格式,例如 CSV、JSON與 XML。你也會看到如何用關聯資料庫來儲存與取出資料,甚至看到一些最近的 NoSQL資料存儲。
第九章
這一章專門討論 Web,包括用戶端、伺服器、資料擷取、API與框架。在第九章,你會使用請求參數與範本來處理一個實際的網站。
第十章
這一章討論核心系統。在這章,你會學習管理程式、流程與執行緒,處理資料與時間,
以及將系統管理工作自動化。
第十一章
這一章的主題是網路:服務、協定與 API。本章的範例包括低階的 TCP通訊端、傳訊程式庫、序列系統,及雲端部署。
第十二章
本章提供一些祕訣給 Python開發人員,包括安裝與使用 IDE、測試、除錯、登入、原始碼控制,及文件化。這一章也會協助你尋找並安裝實用的第三方套件、將你自己的程式
包裝起來,供未來重複使用,並瞭解如何找到更多資訊。祝你好運。
附錄 A
第一個附錄會深入討論 Python在以下領域中扮演的角色:圖形、音樂、動畫,與遊戲。
附錄 B
Python在商務領域中有特定的應用:資料視覺化(繪製、圖表及地圖)、安全與監控。
附錄 C
Python在科學界扮演強大的角色:數學與統計學、物理科學、生物科學,與醫學。附錄C也會提到 NumPy、SciPy與 Pandas。
viii | 前言
附錄 D
如果你尚未在電腦上安裝 Python,這個附錄會教你安裝的方法,無論你用的是Windows、Mac OS/X、Linux或 Unix。
附錄 E
這一章是每章練習題的解答。在你解答之前,不要偷看這個附錄。
附錄 F
這個附錄是可供快速參考的備忘錄。
Python版本程式語言會隨著開發者為它加入新功能與修正錯誤而不斷變化。本書的範例使用 Python 3.3版來編寫與測試。在本書編輯的階段,第 3.4版也被公佈了,所以我會討論它的一些新功能。如果你想要知道 Python被加入什麼,以及何時被加入,可以在 Python的網頁上瀏覽 What’s New in Python(https://docs.python.org/3/whatsnew/)。它是一份技術文
件,對剛要學習 Python的你來說可能有點難懂,但是,如果你將來想要讓你的程式可在安裝各種 Python版本的電腦上運作,這會帶來很大的幫助。
本書編排慣例
本書使用下列的編排規則:
斜體字(Italic)代表新的術語、URL、電子郵件地址、檔案名稱及副檔名。
定寬字(Constant width)用來列舉程式,以及在文章中代表程式的元素,例如變數、函式,及資料類型。
定寬粗體字(Constant width bold)代表要由使用者逐字輸入的指令或其他文字。
定寬斜體字(Constant width italic)代表要換成使用者提供的值,或依上下文而決定的值。
第四章
Py之殼:程式結構
在第一到三章中,你已經看過許多資料的範例,但還沒有認真地使用它們。大多數的範
例程式使用的都是互動式解譯器,而且都很簡短。現在你會看到如何建立 Python 程式
碼結構,而不是只有資料。
許多電腦語言都會使用大括號({與 })等字元,或關鍵字(例如 begin與 end)來標記一段程式。在這些語言中,使用一致的縮排是一種良好的做法,如此一來,可讓自己與
其他人更容易閱讀程式。市面上甚至有一些工具可以整齊地排列你的程式。
當 Guido van Rossum在設計 Python語言的前身時,他認為縮排本身已足以定義程式的結構了,所以決定省去輸入所有的括號。Python的不尋常之處,在於它用空白字元來
定義程式結構。這是新人會發現的第一個地方,而且對曾經用過其他語言的人來說,這
看起來可能會有點奇怪。但是當你稍微編寫 Python之後,事實證明,你會感覺很自然,並且再也不會有這種感受。你甚至會習慣以更少的打字次數來做更多事情。
用 #來註解註解是在程式中一段會被 Python解譯器忽略的文字。你可能會用註解來解釋附近的Python程式、提醒自己要在某個時刻修復某個東西,或任何其他你要做的事情。你要用#字元來製作註解,從它之後,直到該行結束前的所有東西,都是註解的一部分。你通常會看到獨立的一行註解,如下所示:
>>> # 60 sec/min * 60 min/hr * 24 hr/day>>> seconds_per_day = 86400
76 | 第四章
或者,被放在註解對象的同一行:
>>> seconds_per_day = 86400 # 60 sec/min * 60 min/hr * 24 hr/day
#字元有很多名稱:hash、sharp、pound,或 sinister-sounding octothorpe1。無論你怎麼
稱呼它 2,它的效果僅止於它的那一行結尾。
Python沒有多行註解。你必須使用 #來明確地開始每一行註解或段落。
>>> #我可以在這裡說任何 Python的壞話,... #因為了不起的井號... #保護著我。...>>>
但是,如果全能的井號被放在文字字串裡面,它的角色會回復成一般的 #字元:
>>> print("No comment: quotes make the # harmless.")No comment: quotes make the # harmless.
用 \來延續多行如果你合理地精簡行數,將會比較容易閱讀程式。我建議每行的字元最長 80個(不一定要如此)。如果你無法在那個長度內說出想說的一切,可使用延續字元:\(反斜線)。你只要在行尾放上 \,Python的動作就會如同它們在同一行一般。
例如,如果我想要用較小的字串來建立一個長字串,可以一步步地做這件事:
>>> alphabet = ''>>> alphabet += 'abcdefg'>>> alphabet += 'hijklmnop'>>> alphabet += 'qrstuv'>>> alphabet += 'wxyz'
我也可以使用延續字元,一次完成:
>>> alphabet = 'abcdefg' + \... 'hijklmnop' + \... 'qrstuv' + \... 'wxyz'
1 就像你身後的那一個八條腿的綠色怪物。
2 不要叫它,不然它可能會再次出現。
Py之殼:程式結構 | 77
如果 Python運算式跨越多行,你也需要使用延續符:
>>> 1 + 2 + File "<stdin>", line 1 1 + 2 + ^SyntaxError: invalid syntax>>> 1 + 2 + \... 36>>>
用 if、elif與 else來比較到目前為止,本書已討論幾乎所有的資料結構了。我們終於要來討論程式結構的最後一
個部分,準備將資料放入程式中(在上一章討論集合的部分,你已經稍微看過它們了,
希望之前沒有造成任何不便)。我們的第一個範例是這個小型的 Python程式,它會檢查布林變數 disaster的值,並印出對應的文字:
>>> disaster = True>>> if disaster:... print("Woe!")... else:... print("Whee!")...Woe!>>>
if與 else這幾行是檢查某個條件是否為 True的 Python 陳述式(在這裡是 disaster的值)。請記得,print()是 Python的內建函式,它可以印出東西,通常會印在你的螢幕上。
如果你曾經寫過其他的語言,注意你不需要在使用 if做測試時加上括號。
不要說 if (disaster == True)之類的話。但你必須在結尾使用冒號(:)。
如果你跟我一樣,有時會忘記輸入冒號,Python會顯示錯誤訊息。
在測試式底下的每一行 print()都要縮排。我使用四個空格來縮排每一個小段落。雖然你可以使用任何縮排型式,但 Python預期同一段落的程式會使用一致的格式—每一行都必須縮進相同的格數,對齊左邊。建議的格式是使用四個空格,稱為 PEP-8(http://
bit.ly/pep-8)。不要使用 tab,或混合使用 tab與空格,它會攪亂縮排格數。
78 | 第四章
我們在這裡做了很多事情,接下來會完整解釋它們:
• 將布林值 True指派給變數 disaster。
• 使用 if與 else來執行條件比較,根據 disaster的值來執行不同的程式。
• 呼叫 print()函式來印出一些文字
你可以在測試項中加入測試項,依照你的需要來使用任意的階層數目:
>>> furry = True>>> small = True>>> if furry:... if small:... print("It's a cat.")... else:... print("It's a bear!")... else:... if small:... print("It's a skink!")... else:... print("It's a human. Or a hairless bear.")...It's a cat.
在 Python 中,縮排會決定 if與 else段落的配對方式。我們的第一個測試式會檢查furry。因為 furry是 True,Python會前往縮排的 if small測試式。因為我們將 small設為 True,if small相當於 True。這可以讓 Python執行下一行,並印出 It's a cat。
如果你有三個以上的可能性需要測試,可使用 if、elif(意思是 else if)與 else:
>>> color = "puce">>> if color == "red":... print("It's a tomato")... elif color == "green":... print("It's a green pepper")... elif color == "bee purple":... print("I don't know what it is, but only bees can see it")... else:... print("I've never heard of the color", color)...I've never heard of the color puce
在上述範例中,我們使用 ==運算子來測試相等與否。Python的比較運算子有:
相等 ==不相等 !=小於 <
Py之殼:程式結構 | 79
小於等於 <=大於 >大於等於 >=成員資格 in...
它們會回傳布林值 True或 False。我們來看它們是如何動作的,但是在一開始,我們先賦值給 x:
>>> x = 7
現在,我們來測試一些東西:
>>> x == 5False>>> x == 7True>>> 5 < xTrue>>> x < 10True
注意,兩個等號(==)的用途是測試相等與否,一個等號(=)是將值指派給變數。
如果你需要同時做多個比較,可使用布林運算子 and、or與 not來求出最後的布林結果。
布林運算子的優先順序比它們要比較的程式段落還要低。也就是說,程式段落會先被計
算,再做比較。在這個範例中,因為我們將 x設為 7,所以 5 < x會被算出 True,且 x < 10也是 True,所以我們最後得到 True and True:
>>> 5 < x and x < 10True
如同第 25頁的“優先順序”所說的,要避免搞不清楚優先順序,最簡單的方式就是加上括號:
>>> (5 < x) and (x < 10)True
以下是一些其他的測試:
>>> 5 < x or x < 10True>>> 5 < x and x > 10False>>> 5 < x and not x > 10True
80 | 第四章
如果你將多個比較式 and成為一個變數,Python可讓你這麼做:
>>> 5 < x < 10True
它的效果與 5 < x and x < 10一樣。你也可以編寫較長的比較式:
>>> 5 < x < 10 < 999True
什麼是 True?如果我們檢查的元素不是布林會怎樣? Python是如何認定 True與 False的?
false值不一定非得是 False不可。例如,以下都會被視為 False:
布林 Falsenull None整數零 0浮點數零 0空字串 ''空串列 []空 tuple ()空字典 {}空集合 set()
所有其他的東西都會被視為 True。Python程式會使用這一種“true與否”的定義(或在這個例子中,“false與否”)來檢查空的資料結構以及 False條件:
>>> some_list = []>>> if some_list:... print("There's something in here")... else:... print("Hey, it's empty!")...Hey, it's empty!
如果你要測試的是一個運算式,而不是一個簡單的變數,Python會計算運算式,並回傳布林結果。所以,如果你輸入:
if color == "red":
Python會計算 color == "red"。在稍早的範例中,我們指派字串 "puce"給 color,所以color == "red"是 False,因此 Python會前往下一個測試:
elif color == "green":
Py之殼:程式結構 | 81
用 while來重複執行用 if、elif與 else來測試,程式會從上面執行下面。有時我們需要非一次性的東西。我們需要迴圈,而 Python最簡單的迴圈機制就是 while。使用互動式解譯器來嘗試以下範例,它是一個簡單的迴圈,會印出數字 1到 5:
>>> count = 1>>> while count <= 5:... print(count)... count += 1...12345>>>
我們先將 1指派給 count。while迴圈會比較 count的值是否為 5,如果 count小於或等於5,就會繼續執行。在迴圈中,我們印出 count的值,接著使用陳述式 count += 1將它的值加一。Python會回到迴圈的最上面,再次比較 count的值是否為 5。現在 count的值是 2,所以 while迴圈的內容還是會被執行,因此 count被遞增到 3。
它會繼續下去,直到迴圈最下面將 count由 5遞增到 6,在下一次回到最上面時,count <= 5會是 False,所以 while迴圈會結束。Python會前往下一行。
用 break來取消如果你希望迴圈在發生某個情況時停止,但不確定那件事會在什麼時候發生,可以使用
無窮迴圈以及一個 break陳述式。這次我們會用 Python的 input()函式來讀取一行鍵盤輸入的文字,將它的首字母改成大寫,並印出它。我們會在輸入一行只有 q這個字母的時候跳出:
>>> while True:... stuff = input("String to capitalize [type q to quit]: ")... if stuff == "q":... break... print(stuff.capitalize())...String to capitalize [type q to quit]: testTestString to capitalize [type q to quit]: hey, it worksHey, it works
82 | 第四章
String to capitalize [type q to quit]: q>>>
用 continue來跳過有時因為某些原因,你不想要跳出迴圈,只想要跳過下一次的迭代。以下是一個故意寫
成的範例,我們要讀取一個整數,如果它是奇數,就印出它的平方,如果它是偶數就跳
過它。我也在裡面加入一些註解。同樣的,我們會使用 q來停止迴圈:
>>> while True:... value = input("Integer, please [q to quit]: ")... if value == 'q': # quit... break... number = int(value)... if number % 2 == 0: # an even number... continue... print(number, "squared is", number*number)...Integer, please [q to quit]: 11 squared is 1Integer, please [q to quit]: 2Integer, please [q to quit]: 33 squared is 9Integer, please [q to quit]: 4Integer, please [q to quit]: 55 squared is 25Integer, please [q to quit]: q>>>
使用 else來檢查中斷如果 while迴圈正常結束(沒有呼叫 break),控制權會被傳給一個選用的 else。當你要編寫一個 while迴圈來尋找某些東西,並且在找到時馬上跳出來時,可以使用這種做法。如果 while迴圈完成,但沒有找到該物件,就會執行 else:
>>> numbers = [1, 3, 5]>>> position = 0>>> while position < len(numbers):... number = numbers[position]... if number % 2 == 0:... print('Found even number', number)... break... position += 1... else: # break not called
Py之殼:程式結構 | 83
... print('No even number found')
...No even number found
else的用法可能沒那麼容易理解,你可以將它想成中斷檢查器(break
checker)。
用 for來迭代基於很好的理由,Python經常會使用迭代器。迭代器可讓你在不知道資料結構有多大,
以及它們的實作方式的情況下遍歷它們。你甚至可以迭代動態建立的資料,處理不適合
被全部放在電腦記憶體內的資料串流。
在 Python中,以這種方式來逐一查看序列是合法的:
>>> rabbits = ['Flopsy', 'Mopsy', 'Cottontail', 'Peter']>>> current = 0>>> while current < len(rabbits):... print(rabbits[current])... current += 1...FlopsyMopsyCottontailPeter
但是我們有更好、更像 Python風格的方式:
>>> for rabbit in rabbits:... print(rabbit)...FlopsyMopsyCottontailPeter
像 rabbits這種串列是可迭代的 Python物件,另外還有字串、tuple、字典、集合,與一些其他的元素。迭代 tuple或串列時,每次會產生一個項目。迭代字串會一次產生一個字元,如下所示:
>>> word = 'cat'>>> for letter in word:
84 | 第四章
... print(letter)
...cat
迭代字典(或它的 keys()函式)會回傳鍵。這個範例的鍵是桌遊 Clue(Cluedo outside of North America)的卡片類型:
>>> accusation = {'room': 'ballroom', 'weapon': 'lead pipe', 'person': 'Col. Mustard'}>>> for card in accusation: # or, for card in accusation.keys():... print(card)...roomweaponperson
要迭代值,而不是鍵,你可以使用字典的 values()函式:
>>> for value in accusation.values():... print(value)...ballroomlead pipeCol. Mustard
要用 tuple來回傳鍵與值,你可以使用 items()函式:
>>> for item in accusation.items():... print(item)...('room', 'ballroom')('weapon', 'lead pipe')('person', 'Col. Mustard')
請記得,你可以用一個步驟來對一個 tuple賦值。將 items()回傳的 tuple的第一個值(鍵)指派給 card,第二個值(值)指派給 contents:
>>> for card, contents in accusation.items():... print('Card', card, 'has the contents', contents)...Card weapon has the contents lead pipeCard person has the contents Col. MustardCard room has the contents ballroom
Py之殼:程式結構 | 85
用 break來取消for迴圈內的 break會跳出迴圈,它在 while迴圈也是如此。
用 continue來跳過在 for迴圈中插入 continue會跳到下次迴圈迭代,如同它在 while迴圈內的動作。
使用 else來檢查中斷與 while很像,for有一個選用的 else,可檢查 for是否正常結束。如果它沒有執行
break,就會執行 else陳述式。
當你想要確認之前的 for迴圈已完全執行,而不是被 break提早停止時,可使用這個指令。以下範例中的 for迴圈會印出起司的名稱,如果起司店裡面有那一個起司的話,就會中斷迴圈:
>>> cheeses = []>>> for cheese in cheeses:... print('This shop has some lovely', cheese)... break... else: #沒有中斷代表沒有乳酪... print('This is not much of a cheese shop, is it?')...This is not much of a cheese shop, is it?
與 while一樣,同時使用 else與 for可能有點違反直覺。如果你將 for想
成尋找某個東西,在你沒有找到它的時候,就會呼叫 else,這樣會比較容
易理解。如果你想要在不使用 else的情況下,取得相同的效果,可使用
一些變數,來提示你是否在 for迴圈中找到想要的東西,如下所示:
>>> cheeses = []>>> found_one = False>>> for cheese in cheeses:... found_one = True... print('This shop has some lovely', cheese)... break...>>> if not found_one:... print('This is not much of a cheese shop, is it?')...This is not much of a cheese shop, is it?
86 | 第四章
用 zip()來迭代多個序列這裡還有一種很棒的迭代技巧:使用 zip()函式,以並行的方式來迭代多個序列:
>>> days = ['Monday', 'Tuesday', 'Wednesday']>>> fruits = ['banana', 'orange', 'peach']>>> drinks = ['coffee', 'tea', 'beer']>>> desserts = ['tiramisu', 'ice cream', 'pie', 'pudding']>>> for day, fruit, drink, dessert in zip(days, fruits, drinks, desserts):... print(day, ": drink", drink, "- eat", fruit, "- enjoy", dessert)...Monday : drink coffee - eat banana - enjoy tiramisuTuesday : drink tea - eat orange - enjoy ice creamWednesday : drink beer - eat peach - enjoy pie
zip()會在最短的序列結束時停止。這裡面有一個串列比其他的長(desserts),所以除非我們加長其他的串列,否則沒有人可以拿到 pudding。
第 58頁的“字典”告訴你如何使用 dict()函式,以兩個項目的序列來建立字典,例如tuple、串列,或字串。你可以使用 zip()來逐一查看多個序列,並且將同一個位移值的項目做成 tuple。我們來製作兩個英文與法文單字的 tuple:
>>> english = 'Monday', 'Tuesday', 'Wednesday'>>> french = 'Lundi', 'Mardi', 'Mercredi'
接下來使用 zip()來配對這些 tuple。zip()回傳的值本身並不是 tuple或串列,而是一個可迭代的合併值:
>>> list( zip(english, french) )[('Monday', 'Lundi'), ('Tuesday', 'Mardi'), ('Wednesday', 'Mercredi')]
直接將 zip()的結果傳入 dict(),看吧:一個小型的英法字典!
>>> dict( zip(english, french) ){'Monday': 'Lundi', 'Tuesday': 'Mardi', 'Wednesday': 'Mercredi'}
用 range()來產生數字序列range()函式會回傳指定範圍內的一串數字,讓你不需要先建立並儲存一個諸如串列或tuple等大型資料結構。這可以讓你創建廣大的範圍,且不必用到電腦的任何記憶體,讓你的程式當機。
Py之殼:程式結構 | 87
range()的用法很像 slice:range( start, stop, step )。如果你忽略 start,範圍會從 0開始。stop 是唯一必要的值,與 slice一樣,最後一個產生的值是 stop 的前一個。step的預設值是 1,但你可以使用 -1往回走。
如同 zip(),range()會回傳一個可迭代的物件,所以你要用 for ... in來逐一查看值,或將物件轉換成像串列一樣的序列。我們來製作範圍 0、1、2:
>>> for x in range(0,3):... print(x)...012>>> list( range(0, 3) )[0, 1, 2]
以下是製作範圍 2到 0的方式:
>>> for x in range(2, -1, -1):... print(x)...210>>> list( range(2, -1, -1) )[2, 1, 0]
以下的程式會使用 2的 step大小來取得 0到 10間的偶數:
>>> list( range(0, 11, 2) )[0, 2, 4, 6, 8, 10]
其他的迭代器
第八章會介紹迭代檔案的方式。在第六章,你會看到如何迭代自己定義的物件。
生成式
生成式是一種以一或多個迭代器來密集建立 Python資料結構的方式。藉由生成式,你可以結合迴圈與條件測試式,以減少繁瑣的語法。通常會使用生成式就代表你瞭解
Python的程度已經超越初學者等級了。也就是說,你寫的程式更具備 Python風格。