楔子
介绍完类对象之后,我们来介绍实例对象。我们之前费了老鼻子劲将类对象剖析了一遍,但这仅仅是万里长征的第一步。因为Python虚拟机执行时,在内存中兴风作浪的是一个个的实例对象,而类对象只是幕后英雄。
通过class类对象创建实例对象
我们还以之前的代码为例:
class Girl:
name = "夏色祭"
def __init__(self):
print("__init__")
def f(self):
print("f")
def g(self, name):
self.name = name
print(self.name)
girl = Girl()
看一下它的字节码,这里我们只看创建实例对象模的字节码,也就是模块的字节码。
1 0 LOAD_BUILD_CLASS
2 LOAD_CONST 0 (<code object Girl at 0x000002B7A85FABE0, file "instance", line 1>)
4 LOAD_CONST 1 ('Girl')
6 MAKE_FUNCTION 0
8 LOAD_CONST 1 ('Girl')
10 CALL_FUNCTION 2
12 STORE_NAME 0 (Girl)
15 14 LOAD_NAME 0 (Girl)
16 CALL_FUNCTION 0
18 STORE_NAME 1 (girl)
20 LOAD_CONST 2 (None)
22 RETURN_VALUE
我们看到在类构建完毕之后,14 LOAD_NAME这条指令便将刚刚构建的类Girl取了出来、压入运行时栈,然后通过CALL_FUNCTION将栈里面的类弹出、进行调用,得到实例对象,再将实例对象设置在栈顶。18 STORE_NAME将栈顶的实例对象弹出,让符号girl与之绑定,放在local空间中。
所以我们看到调用类对象的指令居然也是CALL_FUNCTION,因为一开始我们说了,类和函数一样,都是要先将PyCodeObject变成PyFunctionObject。
因此执行完毕之后,模块的local空间就会变成这样:
在CALL_FUNCTION中,Python同样会执行对应类型的tp_call操作。所以创建实例的时候,显然执行PyType_Type的tp_call,因此最终是在PyType_Type.tp_call中调用Girl.tp_new来创建instance对象的。
需要注意的是,在创建class Girl这个对象时,Python虚拟机调用PyType_Ready对class Girl进行了初始化,其中一项动作就是继承基类,所以Girl.tp_new实际上就是object.tp_new,而在PyBaseObject_Type中,这个操作被定义为object_new。创建class对象和创建instance对象的不同之处正是在于tp_new不同。创建class对象,Python虚拟机使用的是tp_new,创建instance对象,Python虚拟机则使用object_new。使用类重写__new__的话,应该很容易明白。
因此,由于我们创建的不是class对象,而是instance对象,type_call会尝试进行初始化的动作。
static PyObject *
type_call(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
//......
type = Py_TYPE(obj);
if (type->tp_init != NULL) {
int res = type->tp_init(obj, args, kwds);
if (res < 0) {
assert(PyErr_Occurred());
Py_DECREF(obj);
obj = NULL;
}
else {
assert(!PyErr_Occurred());
}
}
return obj;
}
那么这个tp_init是哪里来的的,是在使用tp_new创建类对象的时候来的,tp_init在PyType_Ready时会继承PyBaseObject_Type的object_init操作。但正如我们之前说的那样,因为A中的定义重写了__init__,所以在 fixup_slot_dispatchers 中,tp_init会指向slotdef中指定的与__init__对应的slot_tp_init。并且还会设置tp_alloc,这与内存分配有关,源码中会有所体现。
static PyObject *
type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
{
//........
//tp_alloc被设置为PyType_GenericAlloc, 表示为实例对象分配内存, 因为内存大小的元信息存在对应的类对象中
//并且在分配内存的同时会将实例对象的ob_type设置为对应的类对象
type->tp_alloc = PyType_GenericAlloc;
if (type->tp_flags & Py_TPFLAGS_HAVE_GC) {
type->tp_free = PyObject_GC_Del;
type->tp_traverse = subtype_traverse;
type->tp_clear = subtype_clear;
}
else
type->tp_free = PyObject_Del;
//设置tp_init
fixup_slot_dispatchers(type);
//......
}
PyObject *
PyType_GenericAlloc(PyTypeObject *type, Py_ssize_t nitems)
{
PyObject *obj;
const size_t size = _PyObject_VAR_SIZE(type, nitems+1);
//分配内存
if (PyType_IS_GC(type))
obj = _PyObject_GC_Malloc(size);
else
obj = (PyObject *)PyObject_MALLOC(size);
if (obj == NULL)
return PyErr_NoMemory();
memset(obj, '