使用者:Xyy23330121/Python/函數注釋


如同本學習資料第一次提到注釋時一樣:在編寫程序時,程序員會不可避免地忘記之前寫的內容。為了讓程序員在讀已經忘記的函數代碼時,可以快速了解函數的內容、使用要點以及可能的、修改函數的方法。需要進行注釋。

本章學習的注釋,也可以由功能比較強勁的文本編輯器讀取、並在使用函數時實時地給程序員以提示。

由於讀者在撰寫的代碼較少時,並沒有作注釋的必要,從而學習動力可能不足。本章為選學。


文檔字符串 編輯

可以通過以下方式,寫入和輸出文檔字符串。

def func():
    """什么都不做

  本函数仅包含一个 pass 语句。
  
  当使用 if / else / def 等语句时,冒号
  的下一行必须是语句组的内容,要进行缩进。
  但单独只有缩进也会导致出错。因此,必须
  要写入至少一个语句。若希望语句组不执行
  任何操作,可以使用 pass 语句来作为语句
  组中唯一的语句。
  
  在使用 pass 语句时,应注意能否有更好的
  方法。比如 if True: pass; else: do so-
  mething的情况。可以直接用 if not True。
"""
    pass

print(func.__doc__)  #输出文档字符串的内容

輸出為:

什么都不做

  本函数仅包含一个 pass 语句。
  
  当使用 if / else / def 等语句时,冒号
  的下一行必须是语句组的内容,要进行缩进。
  但单独只有缩进也会导致出错。因此,必须
  要写入至少一个语句。若希望语句组不执行
  任何操作,可以使用 pass 语句来作为语句
  组中唯一的语句。
  
  在使用 pass 语句时,应注意能否有更好的
  方法。比如 if True: pass; else: do so-
  mething的情况。可以直接用 if not True。

我們接下來學的類 (class) 也可以使用文檔字符串,寫入和輸出的方法是相同的。使用文檔字符串時,應當注意其可讀性。

函數的類型註解 編輯

除上面的「文檔字符串」以外,類型註解也是一種良好的注釋方式。

對於參數的類型註解如果有不了解的地方,可以查閱 Python 文檔。如果查閱 Python 文檔之後還不了解,那就直接省略註解也是可以的。

簡單類型註解 編輯

類型名稱列表
類型名 含義
int 整數
float 浮點數
complex 複數
bool 布爾值
list 列表
tuple 元組
str 字符串
dict 字典
set 集合
frozenset 不可變集合

以下示例展示了對參數的類型進行註解,以及設置默認值的方式。該示例還提示了該函數返回值的類型。

def plus(arg1: int, arg2: int = 0) -> int:
    return arg1 + arg2

這代表着:plus 函數的參數 arg1 應傳入一個 int 類型的值;arg2 有默認值 0,如果要傳入,應傳入一個 int 類型的值。plus 函數應返回一個 int 類型的值。

類型註解僅作為標識,對函數運行無影響。如果我們執行:

print(plus(1.1))

輸出為:

1.1

儘管類型註解提示了應當給參數傳入整數,但傳入浮點數時,函數依舊正常運行。而函數的返回值也並非類型註解提示的整數。

右側表格簡單總結了目前學過的一些類型的名稱。讀者可以查閱 Python 文檔,了解更多的內置類型。

關於類型名稱,我們將在「類」章節中得到更深刻的了解。

類型註解與函數的屬性 編輯

類型註解是函數的一個屬性。我們可以用以下方式輸出註解:

print(plus.__annotations__)

註解的輸出為一個字典:

{'arg1': <class 'int'>, 'arg2': <class 'int'>, 'return': <class 'int'>}

類型註解的作用 編輯

靜態類型檢查器 編輯

類型註解可以用於靜態類型檢查器。靜態類型檢查器會檢查 Python 代碼中各元素的類型,並對於其中類型不正確的內容進行提示和報錯。比如:

def func(s: int) -> None:
    s[index]

在靜態類型檢查器中會提示報錯,因為整數類型不支持索引操作。

文本編輯器 編輯

一些功能強大的文本編輯器會按照類型來提供提示,比如,輸入以下內容後:

a = [0, 1]  #文本编辑器检查这一行,得知 a 的类型
a.

文本編輯器會自動在後面添加一個下拉的提示框。以提示之後的內容可以寫appendclearcopy等。

但這對於函數的參數是沒用的,

def func(a):
    a.

此時,文本編輯器不會提供任何提示。這是因為文本編輯器不能通過賦值表達式知道函數參數的類型。

但如果我們使用:

def func(a:list):  #文本编辑器检查这一行,得知 a 的类型
    a.

文本編輯器就能夠提供提示了。這在許多情況下很有幫助。

類型別名 編輯

有些類型,尤其是複合類型寫起來比較麻煩。此時,我們可以用以下幾種方式創建類型別名:

type 語句 編輯

type vector = list[float]

之後,我們就可以使用名稱 vector 進行類型註解。

簡單賦值語句 編輯

type 語句是 Python 3.12 新增的。為了向下兼容,也可以通過簡單賦值語句創建類型別名:

vector = list[float]

特殊類型 編輯

None 編輯

我們之前提到過,None 是 Python 中代表「無」的量。實際上,它同時也是一個類型。我們可以用 None 來標記函數沒有返回值。例如:

def prtplus(arg1: int, arg2: int = 0) -> None:
    print(arg1 + arg2)

typing.Any 編輯

該類型代表任意類型。它可以說明函數參數可以傳入任意類型,也可以說明函數可以返回任意類型。例如:

from typing import Any

def prttype(arg: Any) -> None:
    print(type(arg))

特別的,對於未進行類型註解的參數或返回值,默認使用 typing.Any 作為其參數類型和返回值類型。

抽象基類 編輯

Python 提供了多種抽象基類(Abstract Base Class,在 Python 文檔中簡稱為 ABC)。這些基類也可以用於判斷類型。

比如,collections.abc.Mapping 表示映射類型,任何具有映射功能的類型,比如字典,都會被判斷為 collections.abc.Mapping 的子類型,也就可以用該類型作為標註:

from collections.abc import Mapping

def func(arg: Mapping) -> None: pass

為了讓代碼的類型在進行檢查時儘可能地適用廣泛,可以使用這種抽象類型。

對於 collections.abc 提到的「抽象方法」等內容,可能要到類的方法與Python的計算章節才會詳細講解。

聯合類型 編輯

可以用|運算符,表示類型是左右兩者之一。

R = int | float           #整数或浮点数
C = int | float | complex #整数或浮点数,或复数

也可以用泛型的方法來作標註:

from typing import Union
R = Union[int, float]           #整数或浮点数
C = Union[int, float, complex]  #整数或浮点数,或复数

泛型 編輯

序列類型、字典、集合等被稱之為容器類型。許多容器類型都支持下標操作,以表示其內部元素的類型。這種下標操作所得到的結果,稱之為泛型。

我們可以用以下方式標註「由某個類型的元素」組成的列表:

a = list[int]  #以多个整数组成的类型。
b = list[int | None]  #以多个“整数或None”组成的类型

類似,可以用 set[int] 來表示由多個整數組成的列表等。


而對於映射結構,比如 dict ,則是用 dict[str, int] 表示「用字符串作索引,得到整數」的字典。


對於 Python 中的大多數容器,類型系統會假定容器中的所有元素都是相同類型的。這表現在代碼上,就是 list[ClassName] 等方式最多僅支持一個參數。由此,我們可以總結出泛型的一般形式:

  1. 一般容器類型:list[int] set[int]
  2. 映射容器類型:dict[str, int] collections.abc.Mapping[str, int]

但元組是一個例外。元組中的元素,在許多代碼中並非相同類型,且其位置很關鍵。所以,我們有以下特例:

特例:元組類型 編輯

可以用以下方式標註「由特定類型元素」組成的元組:

a = tuple[int, str]  #有二个元素的元组,第一个元素是整数,第二个元素是字符串。

為何使用泛型 編輯

之所以使用泛型,是因為沒有很好的方法對容器中元素的類型進行判斷。於是,在輸入:

def func(a: list):
    a[0].

之後,哪怕使用了功能強大的文本編輯器,也不會彈出任何關於 a[0] 所屬類型的提示。

而如果輸入:

def func(a: list[complex]):
    a[0].

文本編輯器就可以進行提示了。它會提示之後的內容可以寫conjugateimagreal等。

泛型的使用 編輯

泛型在使用上和構造出泛型的原類型毫無區別。

array = list[int] #列表泛型
a = array('Python')  #等同于 a = list('Python')
print(a, type(a)) #输出:['P', 'y', 't', 'h', 'o', 'n'] <class 'list'>

可調用類型 編輯

函數就是可調用的類型。我們可以通過以下方式標註可調用的類型。

from typing import Callable

a = Callable[[int], str]       #输入整数参数,返回字符串的可调用类型
b = Callable[[int, int], None] #输入两个整数参数,不返回任何值的可调用类型

特別的,用省略號(三個小數點)可以表示任意參數列表。比如:

from typing import Callable

a = Callable[..., str]       #输入任意参数列表,返回字符串的可调用类型

類對象的類型 編輯

利用 type[ClassName] 的方式,可以要求傳入的參數或返回值的類型是「類」本身或其子類本身。我們在之前的學習中,已經遇到過「類」對象了。比如:

1       #类型为:int
type(1) #类型为:type[int]
int     #类型为:type[int]

在使用時,可以用以下方式:

a = type[int]    #类 int 或其子类
b = type[object] #类 object 或其子类,即任意类型