大师兄的Python源码学习笔记(二十四): 虚拟机中的类机制(三)
大师兄的Python源码学习笔记(二十六): 虚拟机中的类机制(五)
三. 用户自定义Class
- 正如函数声明语句def,类的声明语句class和类虽然是一个逻辑整体,但在编译后他们对应的字节码指令却分离在两个PyCodeObject对象中。
demo.py
class A(object):
name = 'A'
def ___init__(self):
print('A::__init__')
def f(self):
print('A::f')
def g(self,aValue):
self.value = aValue
print(self.value)
a = A()
a.f()
a.g(1)
1 0 LOAD_BUILD_CLASS
2 LOAD_CONST 0 (<code object A at 0x000002832FEB9540, file "demo.py", line 1>)
4 LOAD_CONST 1 ('A')
6 MAKE_FUNCTION 0
8 LOAD_CONST 1 ('A')
10 LOAD_NAME 0 (object)
12 CALL_FUNCTION 3
14 STORE_NAME 1 (A)
13 16 LOAD_NAME 1 (A)
18 CALL_FUNCTION 0
20 STORE_NAME 2 (a)
14 22 LOAD_NAME 2 (a)
24 LOAD_METHOD 3 (f)
26 CALL_METHOD 0
28 POP_TOP
15 30 LOAD_NAME 2 (a)
32 LOAD_METHOD 4 (g)
34 LOAD_CONST 2 (1)
36 CALL_METHOD 1
38 POP_TOP
40 LOAD_CONST 3 (None)
42 RETURN_VALUE
code
1 0 LOAD_NAME 0 (__name__)
2 STORE_NAME 1 (__module__)
4 LOAD_CONST 0 ('A')
6 STORE_NAME 2 (__qualname__)
2 8 LOAD_CONST 0 ('A')
10 STORE_NAME 3 (name)
3 12 LOAD_CONST 1 (<code object ___init__ at 0x000002832FDAD8A0, file "demo.py", line 3>)
14 LOAD_CONST 2 ('A.___init__')
16 MAKE_FUNCTION 0
18 STORE_NAME 4 (___init__)
6 20 LOAD_CONST 3 (<code object f at 0x000002832FEB90C0, file "demo.py", line 6>)
22 LOAD_CONST 4 ('A.f')
24 MAKE_FUNCTION 0
26 STORE_NAME 5 (f)
9 28 LOAD_CONST 5 (<code object g at 0x000002832FEB91E0, file "demo.py", line 9>)
30 LOAD_CONST 6 ('A.g')
32 MAKE_FUNCTION 0
34 STORE_NAME 6 (g)
36 LOAD_CONST 7 (None)
38 RETURN_VALUE
code
4 0 LOAD_GLOBAL 0 (print)
2 LOAD_CONST 1 ('A::__init__')
4 CALL_FUNCTION 1
6 POP_TOP
8 LOAD_CONST 0 (None)
10 RETURN_VALUE
code
7 0 LOAD_GLOBAL 0 (print)
2 LOAD_CONST 1 ('A::f')
4 CALL_FUNCTION 1
6 POP_TOP
8 LOAD_CONST 0 (None)
10 RETURN_VALUE
code
10 0 LOAD_FAST 1 (aValue)
2 LOAD_FAST 0 (self)
4 STORE_ATTR 0 (value)
11 6 LOAD_GLOBAL 1 (print)
8 LOAD_FAST 0 (self)
10 LOAD_ATTR 0 (value)
12 CALL_FUNCTION 1
14 POP_TOP
16 LOAD_CONST 0 (None)
18 RETURN_VALUE
- 声明class A的语句编译后的字节码指令序列存储在与demo.py对应的PyCodeObject对象中。
- classA的实现代码编译后的字节码指令序列存储在A所对应的PyCodeObject对象中。
- 当Python执行引擎开始执行demo.py时,是从与demo.py对应的PyCodeObject对象开始的,第一步就是执行"class A",也就是创建class对象。
1. 创建class对象
1.1 class的动态元信息
- class的元信息指的是关于class的信息,比如class的名称、属性、方法、申请的内存空间大小等。
- 元信息在现代编程语言中是非常重要的概念,也是java、C#中诸如反射等动态性能够实现的基础。
- 在Python中,元信息概念被发挥德淋漓尽致,因此Python也提供了Java、C#等语言所没有的高度灵活的动态性。
- 当demo.py开始执行时,开始的一段字节码如下:
1 0 LOAD_BUILD_CLASS
2 LOAD_CONST 0 (<code object A at 0x000002832FEB9540, file "demo.py", line 1>)
4 LOAD_CONST 1 ('A')
6 MAKE_FUNCTION 0
8 LOAD_CONST 1 ('A')
10 LOAD_NAME 0 (object)
12 CALL_FUNCTION 3
14 STORE_NAME 1 (A)
- 字节码LOAD_BUILD_CLASS将 内建函数
__build_class__
加载进栈顶。该函数为 builtins 模块中的一员,是负责创建类的工具函数。
ceval.c
TARGET(LOAD_BUILD_CLASS) {
_Py_IDENTIFIER(__build_class__);
PyObject *bc;
if (PyDict_CheckExact(f->f_builtins)) {
bc = _PyDict_GetItemId(f->f_builtins, &PyId___build_class__);
if (bc == NULL) {
PyErr_SetString(PyExc_NameError,
"__build_class__ not found");
goto error;
}
Py_INCREF(bc);
}
else {
PyObject *build_class_str = _PyUnicode_FromId(&PyId___build_class__);
if (build_class_str == NULL)
goto error;
bc = PyObject_GetItem(f->f_builtins, build_class_str);
if (bc == NULL) {
if (PyErr_ExceptionMatches(PyExc_KeyError))
PyErr_SetString(PyExc_NameError,
"__build_class__ not found");
goto error;
}
}
PUSH(bc);
DISPATCH();
}
- 接着,虚拟机通过LOAD_CONST将demo.py对应的PyCodeObject和类名A压入栈中,并通过MAKE_FUNCTION指令创建了一个PyFunctionObject对象。
- 在这里,虚拟机所面对的目标从与demo.py对应的PyCodeObject对象,转变为与class A对应的字节码序列:
1 0 LOAD_NAME 0 (__name__) 2 STORE_NAME 1 (__module__) 4 LOAD_CONST 0 ('A') 6 STORE_NAME 2 (__qualname__) 2 8 LOAD_CONST 0 ('A') 10 STORE_NAME 3 (name) 3 12 LOAD_CONST 1 (<code object ___init__ at 0x000002832FDAD8A0, >file "demo.py", line 3>) 14 LOAD_CONST 2 ('A.___init__') 16 MAKE_FUNCTION 0 18 STORE_NAME 4 (___init__) 6 20 LOAD_CONST 3 (<code object f at 0x000002832FEB90C0, file >"demo.py", line 6>) 22 LOAD_CONST 4 ('A.f') 24 MAKE_FUNCTION 0 26 STORE_NAME 5 (f) 9 28 LOAD_CONST 5 (<code object g at 0x000002832FEB91E0, file >"demo.py", line 9>) 30 LOAD_CONST 6 ('A.g') 32 MAKE_FUNCTION 0 34 STORE_NAME 6 (g) 36 LOAD_CONST 7 (None) 38 RETURN_VALUE
- 这里通过LOAD_NAME和STORE_NAME将符号
__module__
和全局名字空间中符号__name__
对应的值关联起来,并放到f_locals中。- 通过STORE_NAME和RETURN_VALUE保存命名空间,并将f_locals压入运营时栈,传给CALL_FUNCTION。
- 最后,虚拟机再次通过LOAD_CONST将类名A和基类object压入栈中,并通过CALL_FUNCTION创建了一个PyFrameObject对象,并通过STORE_NAME保存命名空间。
- 在上面的代码中,创建的所有有用的东西全都被放到了local名字空间(也就是f_locals)中,这里存放的就是class A的元信息(动态元信息)。
1.2 metaclass
- Python中的class对象是调用metaclass对象创建的。
1.2.1 获得metaclass
- 在上面的代码结束部分,会调用之前压入栈中的内建函数
__build_class__
。
Python\bltinmodule.c
static PyObject *
builtin___build_class__(PyObject *self, PyObject *const *args, Py_ssize_t nargs,
PyObject *kwnames)
{
PyObject *func, *name, *bases, *mkw, *meta, *winner, *prep, *ns, *orig_bases;
... ...
meta = _PyDict_GetItemId(mkw, &PyId_metaclass);
if (meta != NULL) {
Py_INCREF(meta);
if (_PyDict_DelItemId(mkw, &PyId_metaclass) < 0) {
Py_DECREF(meta);
Py_DECREF(mkw);
Py_DECREF(bases);
return NULL;
}
/* metaclass is explicitly given, check if it's indeed a class */
isclass = PyType_Check(meta);
}
}
if (meta == NULL) {
/* if there are no bases, use type: */
if (PyTuple_GET_SIZE(bases) == 0) {
meta = (PyObject *) (&PyType_Type);
}
/* else get the type of the first base */
else {
PyObject *base0 = PyTuple_GET_ITEM(bases, 0);
meta = (PyObject *) (base0->ob_type);
}
Py_INCREF(meta);
isclass = 1; /* meta is really a class */
}
if (isclass) {
/* meta is really a class, so check for a more derived
metaclass, or possible metaclass conflicts: */
winner = (PyObject *)_PyType_CalculateMetaclass((PyTypeObject *)meta,
bases);
if (winner == NULL) {
Py_DECREF(meta);
Py_XDECREF(mkw);
Py_DECREF(bases);
return NULL;
}
if (winner != meta) {
Py_DECREF(meta);
meta = winner;
Py_INCREF(meta);
}
}
... ...
return cls;
}
- 在
__build_class__
中会判断类有没有通过metaclass创建,如果是,则通过_PyType_CalculateMetaclass获取metaclass中的元信息(静态元信息):
Objects\typeobject.c
/* Determine the most derived metatype. */
PyTypeObject *
_PyType_CalculateMetaclass(PyTypeObject *metatype, PyObject *bases)
{
Py_ssize_t i, nbases;
PyTypeObject *winner;
PyObject *tmp;
PyTypeObject *tmptype;
/* Determine the proper metatype to deal with this,
and check for metatype conflicts while we're at it.
Note that if some other metatype wins to contract,
it's possible that its instances are not types. */
nbases = PyTuple_GET_SIZE(bases);
winner = metatype;
for (i = 0; i < nbases; i++) {
tmp = PyTuple_GET_ITEM(bases, i);
tmptype = Py_TYPE(tmp);
if (PyType_IsSubtype(winner, tmptype))
continue;
if (PyType_IsSubtype(tmptype, winner)) {
winner = tmptype;
continue;
}
/* else: */
PyErr_SetString(PyExc_TypeError,
"metaclass conflict: "
"the metaclass of a derived class "
"must be a (non-strict) subclass "
"of the metaclasses of all its bases");
return NULL;
}
return winner;
}
-
metaclass中的元信息包含所有class对象应该如何创建的信息。
1.2.2 调用metaclass
- 获得metaclass后,虚拟机调用PyObject_CallFunctionObjArgs函数完成调用metaclass的动作,从而完成class对象的创建。
Objects\call.c
PyObject *
PyObject_CallFunctionObjArgs(PyObject *callable, ...)
{
va_list vargs;
PyObject *result;
va_start(vargs, callable);
result = object_vacall(callable, vargs);
va_end(vargs);
return result;
}
- 最终,PyObject_CallFunctionObjArgs的参数会打包到一个Tuple对象中,并进入PyObject_Call函数:
Include\object.h
typedef PyObject * (*ternaryfunc)(PyObject *, PyObject *, PyObject *);
Objects\call.c
PyObject *
PyObject_Call(PyObject *callable, PyObject *args, PyObject *kwargs)
{
ternaryfunc call;
PyObject *result;
/* PyObject_Call() must not be called with an exception set,
because it can clear it (directly or indirectly) and so the
caller loses its exception */
assert(!PyErr_Occurred());
assert(PyTuple_Check(args));
assert(kwargs == NULL || PyDict_Check(kwargs));
if (PyFunction_Check(callable)) {
return _PyFunction_FastCallDict(callable,
&PyTuple_GET_ITEM(args, 0),
PyTuple_GET_SIZE(args),
kwargs);
}
else if (PyCFunction_Check(callable)) {
return PyCFunction_Call(callable, args, kwargs);
}
else {
call = callable->ob_type->tp_call;
if (call == NULL) {
PyErr_Format(PyExc_TypeError, "'%.200s' object is not callable",
callable->ob_type->tp_name);
return NULL;
}
if (Py_EnterRecursiveCall(" while calling a Python object"))
return NULL;
result = (*call)(callable, args, kwargs);
Py_LeaveRecursiveCall();
return _Py_CheckFunctionResult(callable, result, NULL);
}
}
- 由于Py_Type的ob_type还是指向Py_Type,所以最终将调用到Py_Type的tp_call操作:
Objects\typeobject.c
static PyObject *
type_call(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
PyObject *obj;
... ...
obj = type->tp_new(type, args, kwds);
... ...
return obj;
}
- 综上所述,如果对象定义了tp_call操作,就是可调用的(callable)。