用 Python 快寫程式

Python 的哲學是:「每件事的做法最好只有一種,而且既簡單又明確的那一種,是最漂亮的做法!」所以 Python 不需要學習各式各樣花俏的程式寫法,幾招樸實的做法,就能完成各式各樣的任務。招數不多便容易學成,招式簡單便容易使用,自然是易學易用了。

易學易用的特性,讓 Python 在 21 世紀 10 年代,成為最流行的程式語言!你能用它寫好絕大部分的事,而且解決問題的過程並不費力,這讓 Python 超越之前所有熱門程式語言,帶動人人都該學程式設計的熱潮。Python 適合做為程式設計新手的第一個語言,也很適合做為程式設計老手的主力語言,這樣的魅力確實難以抗拒。

但 Python 做為汎用語言,依然有比不上專用語言的先天缺陷!汎用就是你能用 Python 簡單不費力地做好絕大部分的事,但只是堪用,不會比其他專用語言表現得更好。所以選擇 Python 的你,不要因為他最流行、最熱門,便迷信自己所選的是萬靈丹,Python 還是有不靈光的時候。

這時請發揮它膠水語言(glue language)的特色,接合其他程式方案解決問題,而不是有我一個 Python 就好~


準備


下載與安裝 CPython

Python 有多家直譯器,其中官方開發的是 CPython,請至 http://www.python.org 下載安裝程式。

安裝時,勾選「Add Python 3.x to PATH」就不用自己設定系統變數了。


Hello, world!

用記事本寫入如下程式,並儲存檔案為 main.py:

notepad('main.py','print(\'Hello, world!\')')

進入命令提示字元,切換到 main.py 位置,輸入 python main.py,Python 安裝正常的話會顯示 Hello, world!:

cmd('C:\\Users\\User\\Desktop>python main.py','Hello, world!','','C:\\Users\\User\\Desktop>_')


Python shell

輸入 python 後面不接原始碼檔案,會進入 Python shell,一個能夠用 Python 語言操作系統的環境。

你可以一邊使用 help('指令') 查詢指令,一邊撰寫 Python 程式。或使用 dir('物件') 列出功能來操作。

通常會使用到 os 模組,與作業系統聯繫,例如取得作業系統的環境變數:

>>> import os
>>> print(os.environ['PATH'])

或者調用作業系統的指令:

>>> os.system['cls']
想結束 Python shell 請輸入 exit()Ctrl+C


目錄


教學資源

Python 官方的入門教程《The Python Tutorial》已有中文翻譯,自然是學習的首選!還可以下載到自己電腦隨時閱讀:http://docs.python.org/zh-tw/3/download.html,真覺得我之前寫的這些教學範例是多餘的 XD

其次是「菜鸟教程」用範例一步驟一步驟教學的《Python 3 教程》,它像 1998 年代電腦人(PCuSER)推出的 Tips 系列叢書,去掉大量說明文字,用圖解把重點 step by step 教給你。

學會 Python 3 以後,再看《Python 慣用語》學習成熟的 Python 程式寫作技巧,然後懂得查《The Python Standard Library》發揮 Python 內建程式庫的功能,就搞定一切了。

愛上 Python 後,你會很享受《Effective Python 心得筆記》的內容。


基礎範例集

資料與輸出入
運算
陣列
敘述、區塊、段落
流程
迭代
函式
類別
模組
Docstring
異常(Exception)
推斷(Assertion)
開頭宣告與設定
字串格式化
賦值運算式(Assignment expressions)


應用範例集

bytes
str
tuple
list
set
dict
處理可迭代資料
處理物件與變數
處理算式
格式化數字
讀寫檔案
breakpoint


進階範例集

tkinter 圖形化介面
os, shutil 作業系統與檔案系統
sys 直譯器的系統環境與功能設置
ctypes 調用 DLL 程式功能
time 日期與時間的表示法
datetime 計算日期與時間
calendar 月曆
random 亂數產生器
re 匹配資料
sqlite3 資料庫系統
csv 字符分隔表
pickle 序列化(二進制檔案)
json 序列化(純文字格式)
xml.etree.ElementTree 剖析 XML 檔案
tarfile, gzip, bz2, lzma 檔案封裝與壓縮
zipfile 最通用壓縮格式
base64 字元轉碼
logging 日誌檔
pdb 除錯器
socket 網路連線
http.server 自帶網站伺服器能力
cgitb 獲取執行異常訊息
cgi 接收 GET 或 POST 資料
urllib.rulparse, urllib.urlunparse 網址的剖析
http.cookies 客端資料儲存
codecs 解決 Windows 的 UTF-8 亂碼問題
smtplib, smtpd 寄發電子郵件
webbrowser 啟動網頁瀏覽器
urllib.request 程式端網頁存取
xmlrpc.server, xmlrpc.client 遠端呼叫


延伸主題選

Pythonic:這樣寫才 Python!
Python 編程風格指南
Pyton 之禪
Effective Python 中文版目錄
Web Service … Python Wiki
Pygame


附錄

Python 3 指令一覽表
Python 3 內建函式一覽表
底線命名原則


資料與輸出入


輸入、輸出、變數

Example 1


請輸入你的名字:Twideem
哈囉 Twideem,這是你第 1 次參訪。
再見 Twideem,歡迎下次再來~

Example 2


AAA     BBBCCC

AAA BBB CCC
AAA:BBB:CCC

Example 3


(會將 ABC 保存在 text.txt,而且不會顯示 ABC。)


資料


123
123
123
123
b'{'
hello
\n

資料本身就是物件,所以可以直接對資料操作物件的功能:


abc


型態


<class 'int'>


轉換


123
123.0
0b1111011
0o173
0x7b
123
{
65
'\u54c8\u56c9'
False

用 bin() oct() hex() 轉出來的是字串,因此無法直接拿來做數學四則運算,必須先將字串轉為整數:


0b11110110
既然是字串,就可以用 [2:] 擷取 0b 以後的文字,獲得 11110110。

很少需要將整數轉為其他進位的數來計算,平常都是直接拿 0b 0o 0x 開頭的資料計算就好,會將十進位整數轉為其他進位的數,通常都是為了看轉換結果而已,而不是拿來計算,所以使用字串提高輸出的效率很合理。


True 與 False

底下資料為 False:

'' 0 [] () {} set() None

除此之外都是 True。


運算


數學運算


13
7
30
3.3333333333333335
1
3
1000

Python 沒有 ++ 和 -- 之類的功能,但是有 +=-= 之類的可用。


邏輯運算


False
True
False


關係運算


False
True
False
True


位元運算


0b1001
0b1111
0b110
0b10010
0b100
-0b1010


函數計算


123
(4+5j)
(3, 2)
8
10


陣列

其實 Python 沒有陣列,是用 list 物件來滿足同樣的功能,而且做得更多。


Example 1


AAA
BBB
CCC


Example 2


['AAA', 'BBB', 'CCC']
['ABC', 'ABC', 'ABC']
['AAA', 'BBB', 'CCC', 'ABC', 'ABC', 'ABC']


Example 3


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


Example 4


False


敘述、區塊、段落


敘述與區塊

一段完整的程式叫做「敘述(statment)」,連續個敘述叫做「區塊(block)」。

在 Python 每一行就表示一個敘述,因此你不能跳行寫程式,每一句完整程式就是要在一行寫完。

Python 使用縮排表示區塊,同樣縮排的連續個敘述,視為同一區塊。至於要怎樣縮排沒有硬性規定,吃飽沒事用「一個 Tab 字元 + 八個空白字元」這種方式來縮排也可以,只要每行敘述都以同樣方式縮排即可。官方建議使用四個空白字元縮排。

如果執行程式時看到 IndentationError 訊息,就表示不符合縮排規定。

其它程式語言經常使用 ; 表示一段敘述,所以在下 ; 符號之前,程式碼可以不斷換行寫下去。然後使用 {} 或 begin end 將區塊包起來,在範圍之內的程式就算亂排也是同一區塊。相比之下 Python 的規定不甚自由,但犧牲自由換來乾淨整潔、讀起來甚為舒適的程式碼,還是很值得的。其它程式語言一定要用 ; 或 } 作結尾,再怎麼縮排也無法像 Python 乾淨、美觀,可是羨慕得流口水呢~

但靠縮排表示區塊也有缺點,像 lambda 因此只能寫一個敘述,沒辦法像其他語言直接用匿名函數寫一整塊程式來執行,詳情請看匿名函數的範例。


段落

凡事總有例外的時候,如果非得打破一行代表一段敘述、同樣縮排代表一個區塊的規定怎麼辦?


流程


條件

if.. elif.. else


大於零

Conditional expression

if.. else.. 有專屬語句可用,句型是:

肯定運算式 if 條件 else 否定運算式

if 成立的話,會執行前面的運算式,否則執行 else 後面的運算式:


100


迴圈

while


請輸入 1- 10 數字:5
4
3
2
1

break、continue


請輸入密碼:ABC
請輸入密碼:123
請輸入密碼:ABC123
帳號登入中,請稍候…


迭代


for.. in..

Python 的 for 並不是 C 語言的 for 迴圈,而是 C Shell Script 的 foreach 迭代,只能搭配 iterator 使用。

不過,寫過傳統結構化程式設計的人,看到 for 就想拿來跑計數器迴圈,所以最常用到的 iterator 是 range(),產生連續數值讓 foreach 迭代。但別忘了 list 或 generator 之類的可迭代資料,也是 foreach 的迭代對象,可別又拿 range() 當索引值去迭代它們 XD

Example 1


0
1
2
3
4

Example 2


1
3
5
7

Example 3


AAA
BBB
CCC

Example 4


0 AAA
1 BBB
2 CCC

Example 5


1 AAA
2 BBB
3 CCC


for.. else..

for 可以使用 else,但容易誤解的是,它並不是找不到的話才執行,而是迭代完再執行:


1
2
3
end

你可以在 for 裡面用 break 來跳過 else 的程式。


List comprehension expression

for.. in.. 有專屬語句,句型是:

已傳元素運算式 for 元素 in 串列 未傳元素運算式

先看後面的「未傳元素運算式」,在這裡可以對正在迭代而還沒傳回的元素進行運算,例如用 if n%2==0 篩選出能夠整除的偶數,或者再用 for.. in.. 變成多維陣列的迭代句。前面的「已傳元素運算式」可以對 for 每次迭代出來的項目進行運算,例如 n*3 將結果通通乘以三倍:


[6, 12, 18, 24]
使用 list comprehension expression 所獲得的資料是 generator 物件,這是一種可迭代資料(iterable)的物件,所以上面的範例,前後用 [] 包住,讓 print() 以 list 資料輸出,可視場合改用 {} 或 () 或不包,這不是語法結構的一部分。

重點是要操作 generator 物件來使用迭代句,例如在前面加上 * 符號,直接將資料拆解出來:


<generator object <genexpr> at 0x0000024E48B9BFC0>
1 4 9


函式


具名函式


123


預設參數


AAA BBB
AAA BBB CCC
111 222 333


不定參數


(1, 3, 5)
{'a': 1, 'b': 3, 'c': 5}


順位參數

參數可以使用 / 符號,在這符號之前的參數,只能像傳統程式設計的函式一樣,照位置順序傳入使用,無法再使用關鍵字引數:


111 222 333
333 222 111
TypeError: f() got some positional-only arguments passed as keyword arguments: 'a, b, c'

/ 只對前面的參數產生限制,後面的參數則可正常使用,因此可設計出混搭照位置順序使用的參數與用關鍵字選擇性使用的參數。


函式注釋

函式的參數,可以用 : 符號註釋:


匿名函數

用 lambda 指令即時跑一個匿名函數:


Hello
礙於一行一敘述、又無法縮排寫多行程式的限制,lambda 只能寫短短一行程式。唯一解決方式是另外寫個具名函式來用,有夠鳥的 (σ゚∀゚)σ゚∀゚)σ゚∀゚)σ


579
那些靠 } 或 end 表示區塊結束的語言,只要沒結束區塊前,要塞多少行程式給 lambda 都無所謂,反而沒這個問題 o(*≧▽≦)ツ┏━┓


產生器


AAA
BBB
CCC

yield 跟 return 一樣,用來傳回資料,但不會結束函式,程式會繼續往下執行,而且可以再用 yield 傳出資料,讓函式可以傳回多筆資料。善用這項特性,可以讓程式碼有更多樣的設計。

yield 能傳回多筆資料,是因為使用了像 list 一樣可以連續保存資料的 generator。所以用 yield 傳回資料的話,函式的型態就是 generator,不像 return 傳回 'ABC' 時函式是字串型態,傳回 123 時是整數型態;yield 不管傳回 'ABC' 或 123 都是 generator 物件型態。也因此,使用 yield 的函式,大家會稱為產生器(Generator)。

由於 generator 是種 iterable 資料,因此除了可以迭代,也可使用 * 符號拆解資料:


AAA BBB CCC
初涉入 Python 的人,最感到棘手的,正好就是產生器的資料不知如何使用!幾乎函式丟出的資料都是產生器,輸出時總顯示說這是物件,卻又不知道如何從中取得資料,尤其是沒辦法用陣列的索引值方式去取出資料,最讓其它語言跳過來的人感到困擾。所以底下特別示範一下用法,遇到這問題時別困擾太久:


e d c b a
e d c b a
e d c b a
e d c b a


類別


封裝、繼承、多型


333
類別裡的函式,一定要有第一個參數,通常命名為 self。

__ 開頭的變數,無法從外部取用,達到封裝資料的目的。不以 __ 開頭的變數,就可以從外部存取。


多重繼承


AAA
BBB
CCC


函式多載

Python 沒有參數不同就視為不同函式的功能,通常用預設參數的方式來表現,但還是沒有多載那樣方便:


Hello.
Ahoy!
Holla, if ya hear me.


運算子覆載


*****
其它可以覆載的符號有:__sub____mul____matmul____truediv____floordiv____mod____divmod____pow____lshift____rshift____and____xor____or__,以及不是符號,但覆載運算子時常一起覆載的 __str__,即物件導向程式設計常見的 toString()。


模組


Module

每個原始碼檔案就是一個模組(module),可在其它原始碼檔案用 impoer 指令載入使用。解譯器載入模組時,會編譯成 *.pyc 檔案,讓該模組再被使用時提升執行效率。

module.py

sample.py


哈囉范笠衣,歡迎使用模組。
哈囉范厲惡,歡迎使用模組。
哈囉范莉珊,歡迎使用模組。
哈囉范力士,歡迎使用模組。


Package

用資料夾整理這些模組叫做套件(package),每個資料夾可以建立名為 __init__.py 的檔案,用來處理初始化工作,因為解譯器在載入時會先找看看有沒有 __init__.py 可以執行。

package/__init__.py

package/module.py

sample.py


歡迎使用本套件。
感謝使用本模組。


Docstring

每個 function、class、module 都有 __doc__ 屬性,可做為像是 help() 用來說明的文件內容。不過這 __doc__ 屬性的內容是怎麼來的?可以在 function、class、module 的開頭,使用三引號多行字串來輸入:


Help on function do_nothing in module __main__:

do_nothing()
    什麼都不做,有意見嗎?

雖然這看起來很像寓文件於註解的 Doc comment,但 Docstring 純粹就是多行字串,沒有像是 @version、@since、@param、@return 的標記功能。想寫得正式,可遵照 PEP 257《Docstring Conventions》的規範:


Help on function f in module __main__:

do_something(p1='Hello', p2='world')
    這是一個函式。

    參數:
    p1 -- 第一個參數 (預設為 'Hello')
    p2 -- 第二個參數 (預設為 'world')

Hello, world!


異常(Exception)

try.. except.. finally.. 用來設計程式執行期間發生異常的反應,with.. as.. 可以在發生異常時,自動執行一些程式,例如跑 open() 發生異常時,會自動執行 close() 關閉檔案。底下是這兩套指令放在一起使用的範例:


想捕捉特定異常的話:


除了接收異常,我們也可以發出異常,用 raise 即可:


發生異常
想發出特定異常的話:


RuntimeError


推斷(Assertion)

assert 可以檢查資料是否符合某種結果,若不符合,程式會發出 AssertionError


2
4
7
Traceback (most recent call last):
  Fiel "sample.py", line 5, in <module>
    assert digit < 10, 'digit 變數來到十位數了'
AssertionError: digit 變數來到十位數了


開頭宣告與設定


宣告這是 Python 格式的可執行檔


在 Unix-like 作業系統,會根據文件開頭的這段敘述,判斷該用哪個指令工具執行。在 Windows 作業系統的話,是根據附檔名來判斷用哪個應用軟體開啟,不寫也可以。


設定字元編碼


Python 3 預設使用 Unicode 編碼系統,如果你的程式碼以 Big5 字元編碼保存,程式將無法執行。這時可以如範例設定使用 Big5 字元編碼。

但強烈建議用 UTF-8 保存程式碼,因為 Python 3 特性之一,就是全面改用 Unicode 編碼系統,字串物件與相關函式都針對 Unicode 重新設計,用 Big5 常常會遇到需要轉碼的問題,自找麻煩。


import 並不是開頭宣告

import 可以在任何位置中使用,甚至有些模組的 import 是在 try.. except..,並不是開頭宣告的一環。


字串格式化

有 % 和 {} 新舊兩種字串格式化做法:


Hello, world!
Hello, world!
AAA BBB CCC
Twideem Civs

字串格式化的樣式可以很複雜,雖然可讀性低,但經常能將複雜的邏輯簡單設計出來!功能寫不出來總比看不懂在寫什麼好時,就會覺得這功能很重要 XD


舊的字串格式化更多語法符號

語法:

填充字元 對齊符號 正負號 # 0 寬度 千位數分隔號 .小數位數 型態

填充字元

可為任意英文字母和標點符號。

對齊符號

^ < >,分别表示置中、置左、置右。

正負號

+ -,前者會在正整數前面標上加號,後者在負數前面使用負號。

#

使用 # 符號,會在八進位值前面顯示 0,十六進位值前面顯示 0x。

0

使用 0,會在數字前面填充 0。

寬度

設定格式化字串的長度。

千位數分隔號

, _ 兩種符號可用,會將數字每三位數加上符號。

群組

_ , 兩種符號。

.小數位數

設定要顯示出來的小數位數。

型態

%c字元
%s字串
%d整數
%u無號整數
%o八進位值
%x十六進位值(使用小寫 a~f)
%X十六進位值(使用大寫 A~F)
%f小數(隨後可用數字設定位數)
%e小寫指數
%E大寫指數
%g一般格式化(平常是小數,太長改小寫指數。)
%G一般格式化(平常是小數,太長改大寫指數。)
%p記憶體位址
%%百分比符號

新的字串格式化語法進階功能

可用 ^ < > 分别表示置中、置左、置右,後面可以用數字表示寬度。

可用 : 設定填充的符號或字元,預設為空白字元。


賦值運算式(Assignment expressions)

使用 := 符號,可以在跑運算式的同時指定資料給變數。