python - 如何从 C 模块中实例化自定义对象?
问题描述
我在创建从 C 模块中编写的类(类型)的实例时遇到问题。我写了一个最小的、独立的例子来说明这一点。只需将 spam.c、spamtest.py 和 setup.py 这三个文件复制粘贴到一个目录并运行
$ python setup.py develop && python spamtest.py
我不明白为什么在实例化垃圾邮件实例时不调用基本函数 new() 和 init() 。不用说,这会在实际应用程序中导致大量时间段错误,其中这些函数为新创建的对象分配动态内存。
以下是在 Python 的调试版本下运行 spamtest.py 时发生的情况。注意 new() 和 init() 在从 Python 解释器中实例化垃圾邮件对象时被调用,而不是从 C 中。
(pyenv36d) $ python spamtest.py
---------
From Python
New Spam at 0x7fa7a5ca9280
Init Spam at 0x7fa7a5ca9280
Finalize Spam at 0x7fa7a5ca9280
---------
From C
Finalize Spam at 0x7fa7a5ca92c0
---------
* ob
object : <refcnt 0 at 0x7fa7a5bb6670>
type : bytes
refcount: 0
address : 0x7fa7a5bb6670
* op->_ob_prev->_ob_next
<NULL object>
* op->_ob_next->_ob_prev
object : <refcnt 0 at 0x7fa7a5bb6670>
type : bytes
refcount: 0
address : 0x7fa7a5bb6670
Fatal Python error: UNREF invalid object
Current thread 0x00007fa7a5ff8080 (most recent call first):
Aborted
(pyenv36d) $
模块:
#include <Python.h>
typedef struct {
PyObject_HEAD
} Spam;
static PyObject *new(PyTypeObject *type,
PyObject *args, PyObject *kw) {
Spam *self;
self = (Spam *) type->tp_alloc(type, 0);
fprintf(stderr, "New Spam at %p\n", self);
return (PyObject*)self;
}
static int init(Spam *self, PyObject *args, PyObject *kw) {
fprintf(stderr, "Init Spam at %p\n", self);
return 0;
}
static void finalize(PyObject *self) {
fprintf(stderr, "Finalize Spam at %p\n", self);
}
static PyMethodDef spam_methods[] = {
{NULL, NULL, 0, NULL},
};
static PyTypeObject spam_type = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "Spam",
.tp_basicsize = sizeof(Spam),
.tp_flags = 0
| Py_TPFLAGS_DEFAULT
| Py_TPFLAGS_BASETYPE,
.tp_doc = "Spam object",
.tp_methods = spam_methods,
.tp_new = new,
.tp_init = (initproc) init,
.tp_dealloc = finalize,
};
/* To create a new Spam object directly from C */
PyObject *make_spam() {
Spam *spam;
if (PyType_Ready(&spam_type) != 0) {
Py_RETURN_NONE;
}
spam = PyObject_New(Spam, &spam_type);
PyObject_Init((PyObject *)spam, &spam_type);
return (PyObject *) spam;
}
static PyMethodDef module_methods[] = {
{"make_spam", (PyCFunction)make_spam, METH_NOARGS,
"Instantiate and return a new Spam object."},
{NULL, NULL, 0, NULL}
};
static PyModuleDef spam_module = {
PyModuleDef_HEAD_INIT,
"spam",
"Defines the Spam (time, value) class"
,
-1,
module_methods
};
PyMODINIT_FUNC PyInit_spam(void) {
PyObject *m;
m = PyModule_Create(&spam_module);
if (PyType_Ready(&spam_type) < 0) {
return NULL;
}
PyModule_AddObject(m, "Spam", (PyObject*)&spam_type);
return m;
}
安装程序.py
from setuptools import setup, Extension
spam = Extension('spam', sources=['spam.c'])
setup (
name = 'spam',
version = '0.1',
description = 'Trying to instantiate an object from C',
ext_modules = [spam],
packages = [],
)
垃圾邮件测试.py:
from spam import Spam, make_spam
print("---------\nFrom Python")
s1 = Spam()
del s1
print("---------\nFrom C")
s2 = make_spam()
del s2
print("---------")
解决方案
一个答案是通过调用类型对象来构造你的新对象,就像你从 Python 本身一样。
也就是说,更改make_spam()
为执行以下操作:
/* To create a new Spam object directly from C */
PyObject *make_spam() {
Spam *spam;
if (PyType_Ready(&spam_type) != 0) {
Py_RETURN_NONE;
}
spam = (Spam *)PyObject_CallObject((PyObject *)&spam_type, Py_BuildValue(""));
/* your other init here, assuming you have some */
return (PyObject *) spam;
}
这也让我很困惑。我想知道为什么您的原始代码不起作用,以及是否有更好的方法可以“正确”地执行此操作,但这种方法对我有用。
推荐阅读
- r - 如何在R中按行对数据进行排序
- sql - 使用分组结果从 Join 中删除重复项
- python - 如何使 QLabel 表现得像没有 html 的链接?
- loops - 在哪里初始化变量
- python - 如何计算文件中一行中单词的出现次数?
- r - 如何从列中删除此 []?
- powershell - Powershell 上次登录查询。帮助设置阵列以考虑我所有的域控制器
- php - 是什么导致无法使用 AngularJS 和 Codeigniter 3 在 MySQL 表中插入数据?
- mongodb - 了解 16 MB 限制和数据建模
- pointers - 抽象虚拟接口是 Fortran 推荐的编程习惯吗?