如何将c类型和python类型组合成一个结构?
旧代码使用两个全局变量,如下所示:如何将c类型和python类型组合成一个结构?,python,python-3.x,cython,Python,Python 3.x,Cython,旧代码使用两个全局变量,如下所示: # global variables cdef uint64_t g_num = 0 car_type = None 如何设计一个可以同时包含g_num和car_type的结构? 以下是psudo代码: class Car(object): def __init__(self): car_type = None g_num ??? how to define it as an uint64_t? cdef class
# global variables
cdef uint64_t g_num = 0
car_type = None
如何设计一个可以同时包含g_num和car_type的结构?
以下是psudo代码:
class Car(object):
def __init__(self):
car_type = None
g_num ??? how to define it as an uint64_t?
cdef class Car:
uint64_t g_num
car_type ?? what is the type here?
基本上,我需要将组合类型放入dict中,以便使用以下代码:
d = {}
d['aaa'] = Car()
d['aaa'].g_num = 1
d['aaa'].car_type = 'Compact'
最简单的方法可能是将Cython类定义为:
%%cython
cdef class Car:
cdef public unsigned long long int g_num
cdef public object car_type
Cython在幕后创建属性(因此需要public
)g_num
和car_type
。它负责将g_num
初始化为0
和car_type
初始化为None
。当调用\uu del\uu
时,Cython还负责减少绑定到汽车类型
的对象的引用计数
现在,我们希望它能够正常工作:
>>> car=Car()
>>> car.g_num
0L
>>> car.car_type is None
True
>>> car.car_type="Compact"
>>> car.car_type
'Compact'
python对象(
object
)和c成员(例如int
,long-long-int
等等)的属性之间存在细微的区别:
在第一种情况下,我们获得对python对象的引用,并可以对其进行更改。例如:
>>> car=Car()
>>> car.car_type=[1,2]
>>> lst=car.car_type
>>> lst.append(6) #changes also car.car_type!
>>> car.car_type
[1,2,6]
在第二种情况下,cython将创建一个新的python对象,更改它不会更改为其调用属性的对象的原始成员
在这个简单的示例中,这并不意外,因为“long-long-int”将作为不可变的python整数返回,但我们可以使用一个struct来说明这一点:
%%cython
cdef struct S:
int a
int b
cdef class A:
cdef public S s
>>> a=A()
>>> s=a.s
>>> type(s) #it is a python-dictionary:
dict
>>> s.keys() #member names are the keys:
['a', 'b']
>>> s['a'] #initialized to 0
0
>>> s['a']=100
>>> a.s # the last change didn't propagate back to the object a!
{'a': 0, 'b': 0}
为了证实我的说法,
g_num
和car_type
已初始化(这并不明显)。重要的事情发生在这里:
static PyObject *__pyx_tp_new_4test_Car(PyTypeObject *t, CYTHON_UNUSED PyObject *a, CYTHON_UNUSED PyObject *k) {
struct __pyx_obj_4test_Car *p;
PyObject *o;
if (likely((t->tp_flags & Py_TPFLAGS_IS_ABSTRACT) == 0)) {
o = (*t->tp_alloc)(t, 0);
} else {
o = (PyObject *) PyBaseObject_Type.tp_new(t, __pyx_empty_tuple, 0);
}
if (unlikely(!o)) return 0;
p = ((struct __pyx_obj_4test_Car *)o);
p->car_type = Py_None; Py_INCREF(Py_None);
return o;
}
您可以直接看到,car\u type
在该行中设置为None
p->car_type = Py_None; Py_INCREF(Py_None);
不太明显的是如何将g_num
设置为0
。调用PyTypeObject
时会发生这种情况,因为在此过程中,内存(以及g_num
)是用0
初始化的
通过Py\u CLEAR(p->car\u type)
在\u pyx\u tp\u dealoc\u 4test\u car
中减少了对car\u type的引用:
static void __pyx_tp_dealloc_4test_Car(PyObject *o) {
struct __pyx_obj_4test_Car *p = (struct __pyx_obj_4test_Car *)o;
#if CYTHON_USE_TP_FINALIZE
if (unlikely(PyType_HasFeature(Py_TYPE(o), Py_TPFLAGS_HAVE_FINALIZE) && Py_TYPE(o)->tp_finalize) && !_PyGC_FINALIZED(o)) {
if (PyObject_CallFinalizerFromDealloc(o)) return;
}
#endif
PyObject_GC_UnTrack(o);
Py_CLEAR(p->car_type);
(*Py_TYPE(o)->tp_free)(o);
}