Python 的自省

Python 的自省机制

dir 是 Python 自省的方式之一。
所谓 自省,就是 『知道自己是谁』。
type 这个函数,也算一种自省。
还有一些私有属性,也能反映当前数据对象的状态、性质,比如之前提到过的 __dict__,比如一般实例化后的对象都有一个属性 __class__ 指向其 源Class

isinstance 也是经常用到的函数,isinstance(a, b) 表示 『 a 是不是 从 b 实例化过来的』,一般情况下,b 会是一个 Class。虽然 dict 是一个数据类型,但它也算一个 Class,创建一个 dict 类型的数据,本质上也是一次实例化的过程。

>>> a = dict()
>>> isinstance(a, dict)
True

另外一个重要的自省模块 (module),是 Python 自带的标准库,叫 inspect,可以自行 import 之后,通过 PyCharm 内打开 inspect 的源码看看使用方法,或者通过搜索引擎来了解它的用法。

有必要自省吗

这个问题很有意思,因为我们都知道,答案肯定是 有必要。人生是如此,写程序也是如此。
但问题有趣的地方不在于此,而在于一种反思,相当于自省之上的自省: 我们可以自我反思,但是代码需要反思自己吗?一个代码片段,需要知道自己是谁吗?会不会多此一举?

小题目: 已知一个 dict,以及一个其它 object,希望将 dict 上的 key、value 作为 object 的 property。

the_dict = dict(a=1, b=2, c=3)
class TheObject(object):
    pass
the_object = TheObject()

我们已经知道了, the_object 如果 __dict__ 内有匹配,那么 property 就会从它上面读:

>>> the_object.__dict__.update(the_dict)
>>> the_object.a
1
>>> the_object.b
2
>>> the_object.c
3

如果可以,尽量不要在 __ 开头的私有变量上做太多操作。另外,则是 __dict__ 与 property 并非一一对应的,可能有其它情况的存在。
所以,我们换个角度,使用 setattr:

>>> for k, v in the_dict.items():
...     setattr(the_object, k, v)
...
>>> the_object.__dict__
{'a': 1, 'c': 3, 'b': 2}

我们再换一换题目: 已知一个 object,将其特定的 properties 转化为 dict 类型的数据。

>>> properties = ['a', 'b', 'c']
>>> data = {}
>>> for k in properties:
...     data[k] = getattr(the_object, k, None)
...
>>> data
{'a': 1, 'c': 3, 'b': 2}

有些题目是硬编出来的,虽然体现了知识点,但实际场景中从不会用到。
上面的题目,看似也是如此『华而不实』。但它们是实际场景中有不小的概率,会被用起来。
或许你也会奇怪,这些例子,跟 自省 也没有关系呀?不尽然,getattr、setattr 也算是自省的一部分。

我们作为人类,为什么自省?其实为了改变自己
Python 代码中的自省机制,也是为了改变 (代码) 自己;而改变的前提,要先知道自己是什么。
如果一个 Python 对象没有自省机制对应的话,要去改变它,就要从其内部介入,比如说 the_object 的 xxx 改成 xxx,你去改变它的过程中,就必然先进入了它 (the_object) 的环境中的,而自身的改变,是非常困难的。
代码中,一旦 对象 知道了自己是谁,那么它的起点就不再是原始对象本身了,而是更高一层的逻辑,而 the_object 的 xxx 改成 xxx 就能说成 把 the_object 的 xxx 改成 xxx,换句话说,已经是从外部强制介入了。细细感受一下,两者虽然看起来意思一致,却已是天大的差异。一个是自我改变,一个是外部高级力量介入的改变。

使用 getattrsetattr,其实站在了比当前 object 更高的一个层面。
一个变量名hello 跟一个字符串值hello的,原本是完全不同的东西。
而一旦站在更高的层面,就能把它们统一起来。
这种统一,会让代码的逻辑更加干净,增强美感。
这就是 自省 机制带来的重要好处之一。