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

标签 python python-3.x cython

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

# 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_numcar_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 成员的属性之间存在细微差别(例如 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 整数返回,但我们可以使用结构来说明这一点:

%%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。它发生在 tp_allocPyTypeObject 的 被调用,因为在其过程中内存(以及 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/

相关文章:

python - 如何使 Python 模块可共享?

python - 数据装箱 : irregular polygons to regular mesh

python - 将列表中的任何列表分配给字符串脚本。

python - 检查 Python Boto SimpleDB 的结果集是否为空

python-3.x - python-twitter异常处理

python - 将 C 数组转换为 Python 字节

python - 通过 Cython 调用 inet_pton

python - Python 2.7如何高效优雅的创建目录和子目录?

python - 如何使用 Django Rest Framework JSON API 包含相关资源?

Python Eve - REST API additional_lookup 不工作