- reference: Python 源码剖析
- Python-vm-class
- Python中的对象模型
- 用户自定义class
- 从class对象到instance对象
- 访问instance对象中的属性
1.Python虚拟机中的类机制
a.Python中的对象模型
- Python中的三类对象
type对象:表示Python内置的类型
class对象:表示Python程序员定义的类型
instance对象:表示由class对象创建的实例
- 对象间的关系
is-kind-of:基类与子类的关系
is-instance-of:类与实例之间的关系
(type ‘type’) 和 (type ‘object’)
从type对象到class对象
可调用性,class对象中实现了”call“操作(在Python内部的PyTypeObject中tp_call不为空),则可调用
1 | class A(object): |
- 处理基类和type信息
1 | [typeobject.c] |
- 处理基类列表
1 | [typeobject.c] |
- 填充tp_dict
1 | [typeobject.c] |
- slot与操作排序
slot可以视为表示PyTypeObject中定义的操作
1 | [typeobject.c] |
1 | //slotdefs的排序在init_slotdefs中完成 |
- 从slot到descriptor
1 | [descrobject.h] |
- 建立联系
排序后的结果放在slotdefs中,Python虚拟机可以从头到尾遍历slotdefs,基于每一个slot建立一个descriptor,然后在tp_dict中建立从操作名到descriptor的关联
1 | [typeobject.c] |
1 | //slotptr完成转换 |

- repr重写
1 | class A(list): |
1 | [typeobject.c] |

- 确定MRO(Method Resolve Order)
1 | [mro.py] |

- 继承基类操作
1 | [typeobject.c] |
- 填充基类中的子类列表
1 | [typeobject.c] |
c.用户自定义class
1 | [class_0.py] |
创建class对象
class的动态元信息
1 | [PyCodeObject for class_0.py] |

1 | [PyCodeObject for class A] |
1 | [LOAD_LOCALS] |
1 | [CALL_FUNCTION] |

- metaclass
1 | [BUILD_CLASS] |

- 获得metaclass
1 | [ceval.c] |


- 调用metaclass
1 | [object.h] |

d.从class对象到instance对象
1 | [PyCodeObject for class_0.py] |

创建class对象,Python虚拟机使用的是type_new;而对于instance对象,Python虚拟机则使用object_new
1 | [typeobject.c] |
1 | //由于A重写了__init__,所以在fixup_slot_dispatchers中,tp_init会指向slotdefs中指定的与"__init__"对应的slot_tp_init |

e.访问instance对象中的属性
1 | [PyCodeObject for class_0.py] |
1 | [LOAD_ATTR] |
1 | [object.c] |
1 | #属性访问算法 |
- instance对象中的dict

1 | //PyObject_GenericGetAttr |
1 | //PyObject_GenericSetAttr |
- 再论descriptor
一般而言,对于一个Python中的对象obj,如果obj.class 对应的class对象中存在get、set、delete三种操作,那么obj为Python的一个descriptor
1 | [slotdefs in typeobject.c] |
如果细分,那么descriptor还可以分为如下两种:
data descriptor: type中定义了get和set的descriptor
non data descriptor: type中只定义了get的descriptor

如果待访问的属性是一个descriptor,若它存在于class对象的tp_dict中,会调用其get方法;若它存在于instance对象的tp_dict中,则不会调用其get方法

- 函数变身
1 | //PyFunction_Type中,"__get__"对应的tp_descr_get被设置成了&func_descr_get,意味着A.f实际上是一个descriptor |
1 | //PyMethodObject |
- 无参函数的调用
1 | [ceval.c] |

带参函数的调用
Bound Method(a.f) 和 Unbound Method(A.f)
本质区别在于PyFunctionObject有没有与instance对象绑定在PyMethodObject中, Bound Method 完成了绑定动作,而Unbound Method 没有完成绑定动作
千变万化的descriptor
用descriptor实现static method
1 | [class_2.py] |
1 | [funcobject.c] |
PyStaticMethod_Type,创建的staticmethod实际上也是一个descriptor,在PyStaticMethod_Type中,tp_descr_get指向了sm_descr_get
1 | [funcobject.c] |