PyObject多态的实现

2020-11-25  本文已影响0人  Yucz

PyObject的多态实现

PyObject 是 Python 所有对象的基石,所有其他对象 PyIntObjectPyFloatObject 的行为,当强制转换为 PyObject 时,都可以正确得到结果。

这就很类似于面向对象的多态。而 CPython 是 C 语言的,并没有面向对象的概念。所以本文就从 CPython 的源码,简单分析下 PyObject 多态的实现原理

1. PyObject 的定义
#define PyObject_HEAD           \
    int ob_refcnt;      \
    struct _typeobject *ob_type;

typedef struct _object {
    PyObject_HEAD
} PyObject;

PyObject结构体包含两个成员

2 PyIntObject 和 PyFloatObject 的定义
typedef struct {
    PyObject_HEAD
    double ob_fval;
} PyFloatObject;

typedef struct {
    PyObject_HEAD
    long ob_ival;
} PyIntObject;

可以看出 PyIntObjectPyFloatObject 第一个定义的内容都是 PyObject_HEAD, 所以这两个结构体的前两个成员也是

由于结构体访问成员是通过地址的偏移访问的,而 PyIntObjectPyObjectob_refcntob_typ 的相对位置是一样的,所以当我们把一个 PyintObject* 转成 PyObject* 时,我们可以通过 PyObject* 指针正确拿到 ob_refcntob_type

PyIntObject intObject;
PyObject* object = (PyObject*)(&intObject);
object.ob_type; // 可正确得到结果,因为地址偏移是一样的

然后 PyIntObjectPyFloatObjectob_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_Typeprintfunc 指向的是 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
上一篇下一篇

猜你喜欢

热点阅读