4. 类子符号搜索方式 经典类深度优先,新式类广度优先,dir()显示所有符号
4.1 搜索逻辑
1. obj的__getattribute__
2. 实例的名词空间
3. 类的名词空间
4. 祖先的名词空间
5. obj的__getattr__
主要关注第 25行
对比上图,关注 1-4行与第25行
4.2 dir包括所有__dict__
4.3 类子符号的编辑 子符号在搜索逻辑的最高层空间中被创建,覆盖低优先给符号 写时复制 __slots__限定
当以读访问时,解释器会按照搜索逻辑搜索符号,如果失败则报告异常。搜索逻辑的最高层空间中的符号被创建或修改,覆盖低优先给符号,以实现个性化数据。由于python的继承与实例化均基于符号关联,也就是子符号共用同一块内存。如此以来,python减少了构造内存时的耗时,采用写时复制的技术去实现共用。在此角度上,python对象的构造没有向上构建环节,要比c++轻量的多。
新类的__slot__可以限定类的实例支持的符号(限定实例map的key),见下图:
注意第 17 行,由于限定实例的map,所以mm.b永远不用,也就实现了read-only。
4.4 类子符号的访问控制(@)staticmethod、(@)classmethod、(非)绑定调用、(@)property
4.4.1 函数:
- 静态函数staticmethod与类函数classmethod
主要关注 16-19行
- 绑定调用与非绑定调用
4.4.2 数据:property(fget=None,fset=None,fdel=None,doc=None) _x \ __x \ __x__
注,第3行,__x前的__用来实现私有属性,外部无法调用,见16行。私有的实现方式依靠改名,见21行与26行,所以私有属性并不是真正的私有。
第11行,用property重新包装x,实现对x访问属性的控制。
4.5 解释器如何解释符号 函数、仿函式的__call__子符号
由第 9,17,32,36行可以得知,一个对象的实例可以运行是因为其类型中有__call__子符号。进而可由 fun 进行验证:
5. 定义实体的实现
5.1 Def / Lambda __call__本质 符号搜索local,global
注1、8行与14、17、22、23行的差别,可知python一直贯彻符号搜索机制。
5.2 Class的初始化 __new__ / __del__ / __init__ / super
- __new__ 构造对象的内存实体、
- __init__ 用来初始化对象,即数据填充内存实体,在自己空间中覆盖类中符号
从C++的思路上,__new__更为重要;python的角度上__init__更为实际。因为一个对象的定义或者实例化,不需要向上回溯构造。向上初始化而不可能去向上构造。
5.3 闭包函数 用函数模拟类
在函数名词空间的保用子函数处理函数内部数据,可以表现为类的作用,实现用函数模拟类(更专业的说法是,用函数类模拟数据类)
注,10行fun2只是fun1的一个函数符号;12行表明,fun2只是在自己的空间中增加一个关联相关实体的符号;
29行,由于两个fun1中的a与fun2中的a指向同一个实体,所以实现的了编译维护能力。
5.4 函数的逻辑控制
- if ... elif ... else
- for 循环扫描进入逻辑处理,else 表示循环完毕 与in 联用,调用迭代器
- while
6. 内建符号 函数、模块、类、变量 __doc__ __dict__ ……
6.1 常见用法:
- 列表表达式
- 多元赋值 x,y = 1,2 x,y=y,x
- 连续比较 1 < x < 10
- 字符、input/output、字符解析函数 str/expr、’’’、+、*
6.2 详见附录