如何将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);
}