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("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 中最難以推理的東西之一。