旧代码使用两个全局变量,如下所示:
# global variables
cdef uint64_t g_num = 0
car_type = None
如何设计一个可用于包含 g_num 和 car_type 的结构? 这是伪代码:
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?
基本上,我需要将组合类型放入字典中,以便我可以使用以下代码:
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
。当调用 __del__
时,Cython 还负责减少绑定(bind)到 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 成员的属性之间存在细微差别(例如 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 整数返回,但我们可以使用结构来说明这一点:
%%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_type
设置为 None
p->car_type = Py_None; Py_INCREF(Py_None);
不太明显的是如何将g_num
设置为0
。它发生在 tp_alloc
时PyTypeObject
的 被调用,因为在其过程中内存(以及 g_num
)被初始化为 0
。
通过Py_CLEAR(p->car_type)
在__pyx_tp_dealloc_4test_Car
中减少car_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);
}
关于python - 如何将c类型和python类型组合成一个结构体?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48531575/