如何将c类型和python类型组合成一个结构?

2022-10-02 02:21:30 发布

您现在位置:Python中文网/ 问答频道 /正文

旧代码使用两个全局变量,如下所示:

# global variables
cdef uint64_t g_num = 0
car_type = None

我怎样才能设计一个结构,它可以同时包含g\u num和car\u 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'

Tags: 代码nonetypevariablescar结构globalnumclasscdefaaa全局变量uint64psudo
1条回答
网友
1楼 ·

可能最简单的方法是将Cython类定义为:

%%cython
cdef class Car:
    cdef public unsigned long long int g_num
    cdef public object car_type

幕后Cython创建属性(因此需要publicg_numcar_type。它负责初始化g_num0car_typeNone。当调用__del__时,Cython还负责减少绑定到car_type的对象的引用计数。你知道吗

现在我们希望它能正常工作:

>>> car=Car()
>>> car.g_num
0L
>>> car.car_type is None
True
>>> car.car_type="Compact"
>>> car.car_type
'Compact'

python对象(object)和c成员(例如intlong 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_numcar_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_type被设置为None

p->car_type = Py_None; Py_INCREF(Py_None);

不太明显的是g_num如何设置为0。它发生在调用PyTypeObject^{}时,因为在其过程中,内存(以及g_num)是用0初始化的。你知道吗

car_type的引用通过Py_CLEAR(p->car_type)__pyx_tp_dealloc_4test_Car中减少:

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);
}