Python 入門指南 5.0
單元 17 - 型態標記與檢查資料型態
Python 在 3.5 之前一直都是不需要標記型態 (type annotation) ,直到 3.5 才加入標記型態的方式
def function() -> type
但是要注意這只是標記而已,直譯器 (interpreter) 並不會因為型態不同而挑出錯誤,如果要檢查標記過的型態是否與提供的值吻合,需要借助具有檢查功能 IDE 或型態檢查器 (type checker) 。
型態標記的方式是要讓程式更好除錯 (debug) ,相對也就更好維護,以下先示範變數 (variable) 的型態標記方式
1 2 3 4 5 6 7 8 9 10 11 12 | # 替變數 d 做型態標記
d: int
# 設定變數 d 為整數 10
d = 10
# 印出變數 d
print(d)
# 檔名: check_demo01.py
# 說明:《Python入門指南》的範例程式
# 網站: http://kaiching.org
# 作者: 張凱慶
# 時間: 2023 年 5 月
|
對變數進行型態標記,就是在變數識別字 (identifier) 之後加上冒號,冒號後為型態識別字
2 | d: int
|
下面是設定變數,然後印出變數,執行結果如下
$ python check_demo01.py |
10 |
$ |
參數 (parameter) 的標記方式跟變數一樣,至於函數回傳值 (return value) 如果要做標記型態,那要在函數小括弧後面加上 -> ,然後接上型態識別字,最後接上冒號,以下為簡單例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | # 定義型態標記的函數
def do_something(a: int) -> int:
# 回傳參數 a
return a
# 以下是執行部分
# 印出 do_something() 的回傳值
print(do_something(11))
# 檔名: check_demo02.py
# 說明:《Python入門指南》的範例程式
# 網站: http://kaiching.org
# 作者: 張凱慶
# 時間: 2023 年 5 月
|
第 2 行定義的 do_something() 函數除了標記參數 a 的型態外,也標記了回傳值型態
2 4 | def do_something(a: int) -> int:
return a
|
底下執行部分就是直接印出 do_something() 函數的回傳值,執行結果如下
$ python check_demo02.py |
11 |
$ |
嚴格說,型態標記在 Python 中只是輔助而已,如果希望程式中自動進行型態檢查,這時候需要借助關鍵字 (keyword) assert , assert 會檢查其後的運算式是否為 True ,如果為 False ,就會發起 AssertionError 例外 (exception) 。
底下示範一個比較複雜的例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | # 定義型態標記的函數
def do_something(a: int) -> int:
try:
# 檢查參數 a 是否為整數
assert type(a) is int
except AssertionError:
# 判斷參數 a 是否為浮點數
if type(a) is float:
# 將 a 整數化
a = int(a)
# 判斷參數 a 是否為字串
elif type(a) is str:
# 判斷參數 a 是否為數字字串
if a.isnumeric():
# 將 a 整數化
a = int(a)
# 處理參數 a 不是整數的情況
if type(a) is not int:
a = 0
# 回傳參數 a
return a
# 以下是執行部分
# 印出 do_something() 的回傳值
print(do_something("12"))
print(do_something("ab"))
print(do_something("c22"))
print(do_something("22c"))
print(do_something(5.9))
print(do_something(4+3j))
# 檔名: check_demo03.py
# 說明:《Python入門指南》的範例程式
# 網站: http://kaiching.org
# 作者: 張凱慶
# 時間: 2023 年 5 月
|
do_something() 函數 (function) 需要一個參數並且回傳這個參數,但是先對 a 進檢查,注意由於 assert 可能會發起例外,所以要放到 try 底下
3 5 | try:
assert type(a) is int
|
內建函數 type() 會回傳參數的型態物件,內建型態識別字也是該型態的型態物件,如果兩個是一樣的就不會發生例外。
底下 except 處理發生 AssertionError 例外的情況,依次判斷參數是否為浮點數 (floating-point number) 、字串 (string) 等情況
6 8 10 12 14 16 18 19 | except AssertionError:
if type(a) is float:
a = int(a)
elif type(a) is str:
if a.isnumeric():
a = int(a)
if type(a) is not int:
a = 0
|
簡單說,如果參數 a 不是整數,就會判斷 a 是否為浮點數或數字字串,如果是這兩種情況,就會運用內建函數 int() 轉換為整數,最後如果 a 還不是整數,那就將 a 設定為整數 0 。
底下執行部分是用不同的參數來測試這個新版的 do_something() 函數
26 27 28 29 30 32 | print(do_something("12"))
print(do_something("ab"))
print(do_something("c22"))
print(do_something("22c"))
print(do_something(5.9))
print(do_something(4+3j))
|
執行結果如下
$ python check_demo03.py |
12 |
0 |
0 |
0 |
5 |
0 |
$ |
利用型態檢查的技巧,我們可以在 Python 中實現函數多載 (overloading) 的特性,所謂函數多載是指函數可以有多種參數列 (parameter list) 版本,以下為一個簡單例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | # 定義型態標記的函數
def do_something2(a: int, b: int = None) -> int:
# 判斷 b 是否有值
if b is not None:
# 回傳參數相加值
return a + b
else:
# 回傳參數 a
return a
# 以下是執行部分
# 印出 do_something2() 的回傳值
print(do_something2(10, 12))
print(do_something2(23))
# 檔名: check_demo04.py
# 說明:《Python入門指南》的範例程式
# 網站: http://kaiching.org
# 作者: 張凱慶
# 時間: 2023 年 5 月
|
do_something2() 是將第二個參數 b 預設為 None
2 4 6 7 9 | def do_something2(a: int, b: int = None) -> int:
if b is not None:
return a + b
else:
return a
|
簡單說,如果參數 b 不是 None ,就會將參數 b 列入計算,此例是回傳 a 加上 b 的值,反之只會計算參數 a ,此例是回傳 a 。
底下執行部分就分成兩種呼叫方式,先是提供兩個參數,接著是只有一個參數
13 14 | print(do_something2(10, 12))
print(do_something2(23))
|
執行結果如下
$ python check_demo04.py |
22 |
23 |
$ |
多載的技巧是讓參數列的參數數量及型態有彈性,但需要加入不少額外的程式碼,在 Python 中才能正常運作多載,所以函數需不需要以多載來設計,說到底還是得看程式需求。
接下來我們看到 Python 程式檔案的層次, Python 檔案就叫做模組 (module) ,模組間可以自由引入,然後我們會再討論怎麼組織模組變成套件 (package) ,也就是 Python 的程式庫 (library) 。
中英文術語對照 | |
---|---|
除錯 | debug |
例外 | exception |
浮點數 | floating-point number |
函數 | function |
識別字 | identifier |
直譯器 | interpreter |
關鍵字 | keyword |
程式庫 | library |
模組 | module |
多載 | overloading |
套件 | package |
參數 | parameter |
參數列 | parameter list |
回傳值 | return value |
字串 | string |
標記型態 | type annotation |
型態檢查器 | type checker |
變數 | variable |
重點整理 |
---|
1. 變數或參數如果要做型態標記,是在識別字後面加冒號,後面加上型態識別字,至於函數回傳值做型態標記,是在小括弧後加上 -> ,後面一樣加上型態識別字。 |
2. 如果型態標記要跟除錯結合,需要用到型態檢查器。 |
3. 關鍵字 assert 可用來做型態檢查。 |
4. 函數多載是指函數可以有不同的參數列版本。 |
問題與討論 |
---|
1. 為什麼要做型態標記?做型態標記對除錯有什麼幫助? |
2. 關鍵字 assert 除了用來檢查參數型態外,還可以做什麼? |
3. 什麼是函數多載?為什麼要做函數多載? |
練習 |
---|
1. 承接單元 11 的練習 4 ,將新程式寫在 exercise1701.py 中,替 my_sum() 加入型態標示,並將型態檢查改成 assert 陳述。 參考程式碼 |
2. 承接單元 11 的練習 5 ,將新程式寫在 exercise1702.py 中,替 factorial() 加入型態標示,並將型態檢查改成 assert 陳述。 參考程式碼 |
3. 承接單元 11 的練習 6 ,將新程式寫在 exercise1703.py 中,替產生器函數 factorial() 加入型態標示,並將型態檢查改成 assert 陳述。 參考程式碼 |
4. 承接單元 11 的練習 7 ,將新程式寫在 exercise1704.py 中,替 fibonacci() 加入型態標示,並將型態檢查改成 assert 陳述。 參考程式碼 |
5. 承接單元 11 的練習 8 ,將新程式寫在 exercise1705.py 中,替產生器函數 fibonacci() 加入型態標示,並將型態檢查改成 assert 陳述。 參考程式碼 |
6. 承接單元 14 的練習 4 ,將新程式寫在 exercise1706.py 中,替 Calculator 類別加入型態標示,並將型態檢查改成 assert 陳述。 參考程式碼 |
7. 承接單元 14 的練習 8 ,將新程式寫在 exercise1707.py 中,替 NumberSequence 類別加入型態標示,並將型態檢查改成 assert 陳述。 參考程式碼 |
8. 承接單元 14 的練習 9 ,將新程式寫在 exercise1708.py 中,替 Factorial 類別加入型態標示,並將型態檢查改成 assert 陳述。 參考程式碼 |
9. 承接單元 14 的練習 10 ,將新程式寫在 exercise1709.py 中,替 Fibonacci 類別加入型態標示,並將型態檢查改成 assert 陳述。 參考程式碼 |
10. 承接單元 15 的練習 1 ,將新程式寫在 exercise1710.py 中,替 Point 類別加入型態標示,並將型態檢查改成 assert 陳述。 參考程式碼 |
11. 承接單元 16 的練習 6 ,將新程式寫在 exercise1711.py 中,替 Animal 類別加入型態標示,並將型態檢查改成 assert 陳述。 參考程式碼 |
12. 承接單元 16 的練習 7 ,替 Lion 類別加入型態標示,並將型態檢查改成 assert 陳述。 參考程式碼 |