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 中最难以推理的东西之一。