2.33. 自省

少數幾個內建函式讓執行中的程式能夠檢視自己 -- 它正在處理的值、這些值所在的命名空間,以及類別之間的關係。當你需要根據一個物件實際上是什麼(而非其呼叫者宣稱它是什麼)來做決策時,就動用它們。

2.33.1. 識別性與雜湊

  • id() -- 一個獨一無二的整數,在物件存活期間用以識別它。繫結同一個物件的兩個名稱會傳回相同的 id;兩個相等但相異的物件則不會。

>>> a = [1, 2, 3]
>>> b = a
>>> c = [1, 2, 3]
>>> id(a) == id(b)        # same list
True
>>> id(a) == id(c)        # equal but distinct
False

id 不能跨多次執行而沿用,其意義也僅止於「相同物件或不同物件」。

  • hash() -- 一個物件的雜湊值,也就是 dictset 用來查找它的同一個數字。兩個相等的物件雜湊出相同的值;只有可雜湊的型別(大多是不可變的值)才能運作。

>>> hash("abc")           # some integer, build-dependent
-1600925533
>>> hash([1, 2])
TypeError: unhashable type: 'list'

2.33.2. 查詢型別與可呼叫性

  • type() -- 一個值的確切類別。type(x) is int 問的是「x 是否恰好是 int」(不含子類別);通常你想要的其實是 isinstance()

  • isinstance() -- 「x 是否為這個類別的實例,或其子類別的實例?」是在函式內部進行基於型別之分派的標準工具。

  • issubclass() -- 類別層級的對應工具。它接受兩個類別,而非一個實例。

  • callable() -- 若引數可以用 () 呼叫則為 True。當你收到一個可能是函式、也可能是單純值的引數時很有用。

>>> isinstance(3, int)
True
>>> isinstance(True, int)        # bool is a subclass of int
True
>>> issubclass(bool, int)
True
>>> callable(len)
True
>>> callable(10)
False

一個使用 callable 的模式:

def call_or_return(x):
    return x() if callable(x) else x

2.33.3. 檢視範圍

  • globals() -- 以 dict 形式呈現的模組全域命名空間。從中讀取是可行的;寫入它也確實有效,但在 REPL 探索之外這麼做會讓程式難以理解。

  • locals() -- 呼叫點處的區域命名空間。在函式內部,它反映區域變數;但修改所傳回的字典並不保證能寫回到實際的區域變數中(屬實作定義的行為)。

name = "OpenMV"

def f():
    x = 10
    print(globals()["name"])    # OpenMV
    print(locals())             # {'x': 10}

這兩者對於除錯,以及對於需要探索有哪些定義的工具而言很有用。在一般程式碼中請謹慎動用它們 -- 一個會變動 globals() 的函式是 Python 中最難以推理的東西之一。

2.33.4. 另請參閱

dir()help()(在 debugging 中介紹)是探索未知物件外觀的日常自省工具。