在CPython层传入自定义类型遇到的问题

本文探讨了如何在CPython中通过`ob_type`指针实现自定义字典类型,并揭示了正确使用`tp_call`创建新类型的必要性,以及关于弱引用和tp_weaklistoffset的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

一、CPython对于类型的管理

PyType_Type / PyInt_Type / PyString_Type ....等这些是“类型对象”,都是PyTypeObject的实例!

PyType_Type的类型指向自己, 而且它还是其它类型的“类型”。

二、问题来了

(1)通过上图可以发现底层是通过*ob_type这样一个指针来完成类型的赋值的,那么问题来了,我如果想定义一个自己的dict,是否可以在PyDict_Type的基础上,把它的*ob_type指向自己的dict类型呢?

PyObject* d = PyDict_New();
d->ob_type = custom_dict;

 果不其然,发生了异常!

分析下异常栈:

python27.dll!subtype_dealloc(_object * self) Line 1000 C

type->tp_del(self);

python27.dll!PyObject_ClearWeakRefs(_object * object) Line 925 C

/* Remove the callback-less basic and proxy references */
if (*list != NULL && (*list)->wr_callback == NULL) {
    clear_weakref(*list);
    if (*list != NULL && (*list)->wr_callback == NULL)
        clear_weakref(*list);
}

python27.dll!clear_weakref(_PyWeakReference * self) Line 58 C

if (self->wr_object != Py_None) {
    PyWeakReference **list = GET_WEAKREFS_LISTPTR(self->wr_object);
#define GET_WEAKREFS_LISTPTR(o) \
        ((PyWeakReference **) PyObject_GET_WEAKREFS_LISTPTR(o))
#define PyObject_GET_WEAKREFS_LISTPTR(o) \
    ((PyObject **) (((char *) (o)) + Py_TYPE(o)->tp_weaklistoffset))

 

从这里可以看出更改了ob_type后,Py_TYPE(o)获取到的是custom_dict,原因很可能出在custom_dict和原生的dict中tp_weaklistoffset不一致!

# -*-encoding:utf8-*-

class custom_dict(dict):
	pass

if __name__ == "__main__":
	print custom_dict.__weakrefoffset__  # 128
	print dict.__weakrefoffset__         # 0

也就是析构对象进行弱引用清除的时候,本来tp_weaklistoffset应该是0,加上128后,访问到了非法内存引发的异常。

(2)正确的做法

从CPython的源码中找到了答案,像下面这样,通过tp_call创建自定义的类型,注意在CPython层操作对象需要注意对它的引用计数进行修改。

PyObject* pT = PyTuple_New(0);
d = PyObject_Call((PyObject*)custom_dict, pT, nullptr);
Py_DECREF(pT);

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值