PyObject多态的实现
2020-11-25 本文已影响0人
Yucz
PyObject的多态实现
PyObject 是 Python 所有对象的基石,所有其他对象 PyIntObject
和 PyFloatObject
的行为,当强制转换为 PyObject
时,都可以正确得到结果。
这就很类似于面向对象的多态。而 CPython 是 C 语言的,并没有面向对象的概念。所以本文就从 CPython 的源码,简单分析下 PyObject
多态的实现原理
1. PyObject 的定义
#define PyObject_HEAD \
int ob_refcnt; \
struct _typeobject *ob_type;
typedef struct _object {
PyObject_HEAD
} PyObject;
PyObject结构体包含两个成员
- ob_refcnt: 引用计数,不在本文讨论范围
- ob_type: 每个对象的类型,这是实现多态的关键
2 PyIntObject 和 PyFloatObject 的定义
typedef struct {
PyObject_HEAD
double ob_fval;
} PyFloatObject;
typedef struct {
PyObject_HEAD
long ob_ival;
} PyIntObject;
可以看出 PyIntObject
和 PyFloatObject
第一个定义的内容都是 PyObject_HEAD
, 所以这两个结构体的前两个成员也是
- ob_refcnt
- ob_type
由于结构体访问成员是通过地址的偏移访问的,而 PyIntObject
和 PyObject
的 ob_refcnt
和 ob_typ
的相对位置是一样的,所以当我们把一个 PyintObject*
转成 PyObject*
时,我们可以通过 PyObject*
指针正确拿到 ob_refcnt
和 ob_type
PyIntObject intObject;
PyObject* object = (PyObject*)(&intObject);
object.ob_type; // 可正确得到结果,因为地址偏移是一样的
然后 PyIntObject
和 PyFloatObject
的 ob_type
是不一样的
-
PyIntObject
的ob_type
为PyInt_Type
-
PyFloatObjectf
的ob_type
为PyFloat_Type
而所有对象各自的行为都被封装在各自的类型中
PyTypeObject PyInt_Type = {
PyObject_HEAD_INIT(&PyType_Type)
0,
"int",
printfunc(print_int),
};
PyTypeObject PyFloat_Type = {
PyObject_HEAD_INIT(&PyType_Type)
0,
"float",
printfunc(print_float),
};
可以看出,PyInt_Type
的 printfunc
指向的是 print_int
, 而 PyFloat_Type
指向的是 print_float
, 所以
所以,我们可以通过 PyObject
拿到 ob_type
再去调用里面的东西,从而实现多态
int main()
{
PyIntObject intObject;
intObject.ob_ival = 1;
PyObject_INIT(&intObject, &PyInt_Type);
// 将 PyIntObject 转为 PyObject
PyObject *object1 = (PyObject*)(&intObject);
intObject.ob_type->tp_print(object1, NULL, 0);
PyFloatObject floatObject;
floatObject.ob_fval = 3.33;
PyObject_INIT(&floatObject, &PyFloat_Type);
// 将 PyFloatObject 转为 PyObject
PyObject *object2 = (PyObject*)(&floatObject);
floatObject.ob_type->tp_print(object2, NULL, 0);
return 0;
}
3 完整测试代码
#define PyObject_HEAD \
int ob_refcnt; \
struct _typeobject *ob_type;
#define PyObject_HEAD_INIT(type) \
1, type,
#define PyObject_VAR_HEAD \
PyObject_HEAD \
int ob_size;
typedef struct _object {
PyObject_HEAD
} PyObject;
typedef struct {
PyObject_VAR_HEAD
} PyVarObject;
typedef struct {
PyObject_HEAD
double ob_fval;
} PyFloatObject;
typedef struct {
PyObject_HEAD
long ob_ival;
} PyIntObject;
typedef int (*printfunc)(PyObject *, FILE *, int);
int print_float(PyObject *o, FILE *, int)
{
PyFloatObject *f = (PyFloatObject*)o;
printf("print float: val = %f\n", f->ob_fval);
return 0;
}
int print_int(PyObject *o, FILE *, int)
{
PyIntObject *i = (PyIntObject*)o;
printf("print int: val = %ld\n", i->ob_ival);
return 0;
}
typedef struct _typeobject {
PyObject_VAR_HEAD
const char *tp_name; /* For printing, in format "<module>.<name>" */
printfunc tp_print;
} PyTypeObject;
PyTypeObject PyType_Type = {
PyObject_HEAD_INIT(&PyType_Type)
0, /* ob_size */
"type", /* tp_name */
NULL
};
PyTypeObject PyInt_Type = {
PyObject_HEAD_INIT(&PyType_Type)
0,
"int",
printfunc(print_int),
};
PyTypeObject PyFloat_Type = {
PyObject_HEAD_INIT(&PyType_Type)
0,
"float",
printfunc(print_float),
};
/* Macros trading binary compatibility for speed. See also pymem.h.
Note that these macros expect non-NULL object pointers.*/
#define PyObject_INIT(op, typeobj) \
( (op)->ob_type = (typeobj), (PyObject *)(op), (op) )
int main()
{
PyIntObject intObject;
intObject.ob_ival = 1;
PyObject_INIT(&intObject, &PyInt_Type);
// 将 PyIntObject 转为 PyObject
PyObject *object1 = (PyObject*)(&intObject);
intObject.ob_type->tp_print(object1, NULL, 0);
PyFloatObject floatObject;
floatObject.ob_fval = 3.33;
PyObject_INIT(&floatObject, &PyFloat_Type);
// 将 PyFloatObject 转为 PyObject
PyObject *object2 = (PyObject*)(&floatObject);
floatObject.ob_type->tp_print(object2, NULL, 0);
return 0;
}
打印如下
print int: val = 1
print float: val = 3.330000