跳轉到內容

Think Python/程式設計之道

來自華夏公益教科書

本書的目的是教你像計算機科學家一樣思考。這種思維方式結合了數學、工程和自然科學中一些最好的特點。像數學家一樣,計算機科學家使用形式語言來表示思想(特別是計算)。像工程師一樣,他們設計事物,將元件組裝成系統,並在替代方案之間權衡利弊。像科學家一樣,他們觀察複雜系統的行為,形成假設,並檢驗預測。

對於計算機科學家來說,最重要的技能是 **解決問題**。解決問題意味著能夠制定問題,創造性地思考解決方案,並清晰準確地表達解決方案。事實證明,學習程式設計的過程是練習解決問題技能的絕佳機會。這就是本章被稱為“程式設計之道”的原因。

在一個層面上,你將學習程式設計,這本身就是一項有用的技能。在另一個層面上,你將使用程式設計作為一種手段來達到目的。隨著我們的學習,這個目的將變得越來越清晰。

Python 程式語言

[編輯 | 編輯原始碼]

你將學習的程式語言是 Python。Python 是 **高階語言** 的一個例子;你可能聽說過的其他高階語言包括 C、C++、Perl 和 Java。

還有 **低階語言**,有時被稱為“機器語言”或“組合語言”。簡單來說,計算機只能執行用低階語言編寫的程式。因此,用高階語言編寫的程式必須經過處理才能執行。這種額外的處理需要一些時間,這是高階語言的一個小缺點。

優點是巨大的。首先,用高階語言程式設計要容易得多。用高階語言編寫的程式編寫時間更短,程式碼更簡潔,更易於閱讀,並且更有可能正確。其次,高階語言是 **可移植的**,這意味著它們可以在不同的計算機上執行,幾乎不需要修改。低階程式只能在一種型別的計算機上執行,必須重新編寫才能在其他計算機上執行。

由於這些優點,幾乎所有程式都是用高階語言編寫的。低階語言只用於少數專業應用。

兩種程式將高階語言處理成低階語言:**直譯器** 和 **編譯器**。直譯器讀取高階程式並執行它,也就是說它按照程式的指示執行操作。它一次處理一小段程式,交替讀取行並執行計算。

編譯器讀取程式並在程式開始執行之前將其完全翻譯。在這種情況下,高階程式被稱為 **原始碼**,翻譯後的程式被稱為 **目的碼** 或 **可執行檔案**。一旦程式被編譯,你就可以重複執行它,而無需進一步翻譯。

Python 被認為是一種解釋型語言,因為 Python 程式是由直譯器執行的。使用直譯器有兩種方法:**互動模式** 和 **指令碼模式**。在互動模式下,你輸入 Python 程式,直譯器列印結果

>>> 1 + 1
2

尖括號,>>>,是直譯器用來指示它已準備好的 **提示符**。如果你輸入1 + 1,然後按下回車鍵,直譯器會回覆2.

或者,你可以將程式碼儲存在一個檔案中,並使用直譯器執行該檔案的內容,該檔案被稱為 **指令碼**。按照慣例,Python 指令碼的名稱以.py.

結尾。要執行指令碼,你必須告訴直譯器檔名。在 UNIX 命令視窗中,你會輸入python dinsdale.py。在其他開發環境中,執行指令碼的細節有所不同。你可以在 Python 網站上找到你的環境的說明python.org.

在互動模式下工作對於測試小段程式碼很方便,因為你可以立即輸入並執行它們。但是,對於超過幾行的程式碼,你應該將程式碼儲存為指令碼,以便你可以在將來修改和執行它。

什麼是程式?

[編輯 | 編輯原始碼]

**程式** 是一個指令序列,它指定了如何執行計算。計算可能是數學的,比如解方程組或求多項式的根,但也可能是符號計算,比如在文件中搜索和替換文字或(奇怪的是)編譯程式。

不同的語言細節不同,但以下幾種基本指令幾乎出現在所有語言中

輸入
從鍵盤、檔案或其他裝置獲取資料。
輸出
在螢幕上顯示資料或將資料傳送到檔案或其他裝置。
數學
執行基本數學運算,如加法和乘法。
條件執行
檢查某些條件並執行相應的語句序列。
重複
重複執行某些操作,通常帶有某種變化。

信不信由你,這就是全部。你用過的每一個程式,無論多麼複雜,都是由看起來非常像這些指令的指令組成的。因此,你可以將程式設計視為將一個大型複雜的任務分解成越來越小的子任務的過程,直到子任務足夠簡單,可以用這些基本指令之一來執行。

這可能有點模糊,但當我們討論 **演算法** 時,我們將回到這個話題。

什麼是除錯?

[編輯 | 編輯原始碼]

程式設計容易出錯。出於奇特的原因,程式設計錯誤被稱為 **bug**,追蹤它們的過程被稱為 **除錯**。

程式中可能出現三種類型的錯誤:語法錯誤、執行時錯誤和語義錯誤。為了更快地追蹤它們,區分它們是有用的。

語法錯誤

[編輯 | 編輯原始碼]

Python 只能在語法正確的情況下執行程式;否則,直譯器會顯示一條錯誤訊息。**語法** 指的是程式的結構以及關於該結構的規則。例如,括號必須成對出現,所以(1 + 2)是合法的,但是8)是一個 **語法錯誤**。

在英語中,讀者可以容忍大多數語法錯誤,這就是為什麼我們可以閱讀 E. E. Cummings 的詩歌而不會出現錯誤訊息的原因。Python 並沒有那麼寬容。如果你的程式中存在任何一個語法錯誤,Python 就會顯示一條錯誤訊息並退出,你將無法執行你的程式。在你程式設計生涯的頭幾個星期裡,你可能要花很多時間來追蹤語法錯誤。隨著經驗的積累,你會犯更少的錯誤,並且找到它們的速度也會更快。

執行時錯誤

[編輯 | 編輯原始碼]

第二種型別的錯誤是執行時錯誤,之所以這樣稱呼,是因為這種錯誤直到程式開始執行後才會出現。這些錯誤也被稱為 **異常**,因為它們通常表明發生了異常(並且糟糕的)情況。

在本章中你將看到的簡單程式中,執行時錯誤很少見,所以你可能要等一段時間才會遇到一個。

語義錯誤

[編輯 | 編輯原始碼]

第三種錯誤是語義錯誤。如果你的程式存在語義錯誤,它將成功執行,計算機不會生成任何錯誤訊息,但它不會做正確的事。它會做其他事情。具體來說,它會按照你告訴它做的去做。

問題在於你編寫的程式不是你想要的程式。程式的含義(語義)是錯誤的。識別語義錯誤可能很棘手,因為它需要你透過檢視程式的輸出並試圖弄清楚它在做什麼來反向工作。

實驗性除錯

[編輯 | 編輯原始碼]

你將獲得的最重要的技能之一是除錯。儘管除錯可能令人沮喪,但它卻是程式設計中最具智力豐富、挑戰性和趣味性的部分之一。

在某些方面,除錯就像偵探工作。你面臨著線索,你必須推斷導致你看到的結果的過程和事件。

除錯也像實驗科學。一旦你對錯誤的原因有了想法,你就修改你的程式並再次嘗試。如果你的假設是正確的,那麼你就可以預測修改的結果,並且你離一個可執行的程式更近了一步。如果你的假設是錯誤的,你必須想出一個新的假設。正如夏洛克·福爾摩斯指出的那樣,“當你排除了不可能的事情時,無論剩下什麼,無論多麼不可能,都必須是真相。”(阿瑟·柯南·道爾,《四簽名》)

對某些人來說,程式設計和除錯是一回事。也就是說,程式設計是逐步除錯程式直到它執行你想要的操作的過程。這個想法是你應該從一個做一些事情的程式開始,並進行小的修改,在進行過程中除錯它們,這樣你始終擁有一個可執行的程式。

例如,Linux 是一個包含數千行程式碼的作業系統,但它最初是一個簡單的程式,Linus Torvalds 用它來探索 Intel 80386 晶片。據拉里·格林菲爾德所說,“Linus 早期的專案之一是一個在列印 AAAA 和 BBBB 之間切換的程式。後來發展成了 Linux。”(《Linux 使用者指南》Beta 版 1)。

後面的章節將對除錯和其他程式設計實踐提出更多建議。

形式語言和自然語言

[編輯 | 編輯原始碼]

自然語言是人們使用的語言,例如英語、西班牙語和法語。它們不是由人設計的(儘管人們試圖對它們施加一些秩序);它們是自然演變的。

形式語言是專門為特定應用而設計的人類語言。例如,數學家使用的符號是一個形式語言,特別擅長表示數字和符號之間的關係。化學家使用形式語言來表示分子的化學結構。最重要的是

程式語言是專門為表達計算而設計的人類語言。

形式語言往往對語法有嚴格的規則。例如,3 + 3 = 6 是一個語法正確的數學表示式,但 3 + = 3 $6 不是。H2O 是一個語法正確的化學式,但2Zz 不是。

語法規則分為兩種,分別與符號和結構有關。符號是語言的基本元素,例如單詞、數字和化學元素。3 + = 3 $6 的問題之一是$在數學中不是合法的符號(至少據我所知)。類似地,2Zz 不合法,因為沒有元素的縮寫是Zz

第二種語法錯誤與語句的結構有關;也就是說,符號的排列方式。語句 3 + = 3 $6是非法的,因為即使 + 和 = 是合法的符號,你也不可能一個接一個地使用它們。同樣,在化學式中,下標在元素名稱之後,而不是之前。

練習 1   寫一個結構良好的英語句子,其中包含無效的符號。然後寫另一個包含所有有效符號但結構無效的句子。

當你閱讀英語中的句子或形式語言中的語句時,你必須弄清楚句子的結構(儘管在自然語言中你是下意識地這樣做的)。這個過程稱為解析

例如,當你聽到這句話“The penny dropped”時,你明白“the penny”是主語,“dropped”是謂語。一旦你解析了一個句子,你就可以弄清楚它的意思,或者句子的語義。假設你知道便士是什麼以及它掉下來的意思,你將理解這個句子的普遍含義。

儘管形式語言和自然語言有許多共同點——符號、結構、語法和語義——但也有一些差異

歧義
自然語言充滿了歧義,人們透過使用上下文線索和其他資訊來處理它們。形式語言被設計為幾乎或完全無歧義的,這意味著任何語句都只有一個含義,與上下文無關。
冗餘
為了彌補歧義並減少誤解,自然語言採用了大量的冗餘。因此,它們通常很冗長。形式語言的冗餘度更低,更簡潔。
字面意思
自然語言充滿了習語和隱喻。如果我說,“The penny dropped”,可能沒有便士,也沒有東西掉下來。[1] 形式語言的意思完全是字面意思。

從小就說自然語言的人——每個人——通常很難適應形式語言。在某些方面,形式語言和自然語言之間的差異就像詩歌和散文之間的差異,但程度更甚

詩歌
詞語的音韻和意義都得到了利用,整首詩共同創造了一種效果或情感反應。歧義不僅常見,而且通常是故意的。
散文
詞語的字面意思更為重要,結構貢獻了更多意義。散文比詩歌更容易分析,但仍然經常出現歧義。
程式
計算機程式的含義是明確的和字面意思的,可以透過對符號和結構的分析完全理解。

以下是一些關於閱讀程式(和其他形式語言)的建議。首先,請記住形式語言比自然語言密集得多,因此閱讀它們需要更長的時間。此外,結構非常重要,因此通常從上到下、從左到右閱讀不是一個好主意。相反,學會在腦海中解析程式,識別符號並解釋結構。最後,細節很重要。拼寫和標點符號上的小錯誤,你在自然語言中可以逃避,但在形式語言中卻會有很大的區別。

第一個程式

[編輯 | 編輯原始碼]

傳統上,你在新語言中編寫的第一個程式稱為“Hello, World!”,因為它只做一件事,就是顯示“Hello, World!”在 Python 中,它看起來像這樣

print('Hello, World!')

這是一個列印語句的示例,[2]它實際上並沒有在紙上列印任何東西。它在螢幕上顯示一個值。在這種情況下,結果是單詞

Hello, World!

程式中的引號標記要顯示的文字的開頭和結尾;它們不會出現在結果中。

有些人根據“Hello, World!”程式的簡單性來判斷程式語言的質量。根據這個標準,Python 表現得儘可能好。

最好在電腦前閱讀這本書,這樣你就可以邊讀邊嘗試示例。你可以在互動模式下執行大多數示例,但如果你將程式碼放入指令碼中,則更容易嘗試各種變體。

每當你嘗試一個新功能時,你都應該嘗試犯錯。例如,在“Hello, world!”程式中,如果你省略了一個引號會發生什麼?如果你省略了兩個會發生什麼?如果你拼錯了print呢?

這種實驗可以幫助你記住你讀到的內容;它還有助於除錯,因為你將瞭解錯誤訊息的含義。現在有意識地犯錯誤比以後無意地犯錯誤要好。

程式設計,尤其是除錯,有時會激發出強烈的感情。如果你正在努力解決一個難以解決的錯誤,你可能會感到憤怒、沮喪或尷尬。

有證據表明,人們會自然地將計算機視為真人,並做出相應的反應。[3] 當計算機運作良好時,我們將其視為團隊成員;當它們固執或無禮時,我們也會像對待無禮、固執的人一樣回應它們。

提前做好應對這些反應的準備,可能會幫助你處理它們。一種方法是將計算機想象成一個員工,它擁有某些優勢,比如速度和精度,但也有一些弱點,比如缺乏同理心和無法理解全域性。

你的任務是成為一名優秀的管理者:找到方法來利用這些優勢,並減輕這些弱點的負面影響。同時,也要找到方法利用自己的情緒來解決問題,但不要讓你的反應影響你有效工作的能力。

學習除錯可能會讓人感到沮喪,但這項技能非常寶貴,它在很多活動中都有用,不僅僅是程式設計。在每一章的結尾,都會有一個類似於本節的除錯部分,其中包含我對除錯的一些想法。我希望它們能有所幫助!

術語表

[編輯 | 編輯原始碼]
問題解決
制定問題、尋找解決方案並表達解決方案的過程。
高階語言
像 Python 這樣的程式語言,旨在讓人類易於閱讀和編寫。
低階語言
旨在讓計算機易於執行的程式語言;也稱為“機器語言”或“組合語言”。
可移植性
程式可以在多種型別的計算機上執行的特性。
解釋
透過逐行翻譯來執行高階語言編寫的程式。
編譯
將用高階語言編寫的程式一次性翻譯成低階語言,以備將來執行。
原始碼
編譯之前的高階語言程式。
目的碼
編譯器在翻譯程式後輸出的結果。
可執行檔案
已準備好執行的目的碼的另一種名稱。
提示符
直譯器顯示的字元,表示它已準備好接收使用者的輸入。
指令碼
儲存在檔案中的程式(通常是將被解釋的程式)。
互動模式
在提示符下輸入命令和表示式以使用 Python 直譯器的一種方式。
指令碼模式
使用 Python 直譯器讀取和執行指令碼中語句的一種方式。
程式
指定計算的一組指令。
演算法
解決一類問題的一般過程。
錯誤
程式中的錯誤。
除錯
查詢和移除三種程式設計錯誤中的任何一種的過程。
語法
程式的結構。
語法錯誤
程式中的錯誤,導致程式無法解析(因此也無法解釋)。
異常
程式執行時檢測到的錯誤。
語義
程式的含義。
語義錯誤
程式中的錯誤,導致程式執行的操作與程式設計師的意圖不符。
自然語言
人們自然演化而來的任何一種語言。
形式語言
人們為特定目的而設計的任何一種語言,例如表示數學概念或計算機程式;所有程式語言都是形式語言。
詞法單元
程式語法結構的基本元素之一,類似於自然語言中的單詞。
解析
檢查程式並分析其語法結構。
列印語句
導致 Python 直譯器在螢幕上顯示值的指令。

使用網路瀏覽器訪問 Python 網站:https://python.club.tw/。此頁面包含有關 Python 的資訊以及指向 Python 相關頁面的連結,它還允許你搜索 Python 文件。例如,如果你在搜尋視窗中輸入print,出現的第一個連結就是print語句的文件。目前,你可能無法理解所有內容,但瞭解它的位置很重要。

啟動 Python 直譯器並輸入 'help()' 以啟動線上幫助工具。或者,你可以輸入 help('print') 獲取有關 'print' 語句的資訊。

如果此示例不起作用,你可能需要安裝額外的 Python 文件或設定環境變數;具體細節取決於你的作業系統和 Python 版本。

啟動 Python 直譯器並將其用作計算器。Python 的數學運算語法與標準數學符號幾乎相同。例如,符號 '+'、'-' 和 '/' 分別表示加法、減法和除法,正如你所預期的那樣。乘法的符號是 '*'

如果你跑了 10 公里的比賽,用了 43 分 30 秒,你的平均每英里時間是多少?你的平均時速是多少?(提示:1 英里約等於 1.61 公里)。

參考文獻

[編輯 | 編輯原始碼]
  1. 這個習語的意思是某人在一段時間的困惑之後明白了某個道理。
  2. 在 Python 3.0 中,print 是一個函式,而不是一個語句,所以語法是 print(’Hello, World!’)。我們很快就會講到函式!
  3. 參見 Reeves 和 Nass,The Media Equation: How People Treat Computers, Television, and New Media Like Real People and Places
華夏公益教科書