Python 入門指南 5.0

單元 17 - 型態標記與檢查資料型態

~~學習進度表~~

Python 在 3.5 之前一直都是不需要標記型態 (type annotation) ,直到 3.5 才加入標記型態的方式

variable: type
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) assertassert 會檢查其後的運算式是否為 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 陳述。 參考程式碼

上一頁 單元 16 - 靜態方法與抽象方法
回 Python 入門指南 5.0 首頁
下一頁 單元 18 - 模組與套件
回 Python 教材首頁
回程式語言教材首頁