美文网首页
PyObject多态的实现

PyObject多态的实现

作者: Yucz | 来源:发表于2020-11-25 19:22 被阅读0次

    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结构体包含两个成员

    • ob_refcnt: 引用计数,不在本文讨论范围
    • ob_type: 每个对象的类型,这是实现多态的关键
    2 PyIntObject 和 PyFloatObject 的定义
    typedef struct {
        PyObject_HEAD
        double ob_fval;
    } PyFloatObject;
    
    typedef struct {
        PyObject_HEAD
        long ob_ival;
    } PyIntObject;
    

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

    • ob_refcnt
    • ob_type

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

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

    然后 PyIntObjectPyFloatObjectob_type 是不一样的

    • PyIntObjectob_typePyInt_Type
    • PyFloatObjectfob_typePyFloat_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
    

    相关文章

      网友评论

          本文标题:PyObject多态的实现

          本文链接:https://www.haomeiwen.com/subject/kganiktx.html