Как создать генератор/итератор с помощью API Python C?Python

Программы на Python
Ответить
Anonymous
 Как создать генератор/итератор с помощью API Python C?

Сообщение Anonymous »

Как мне реплицировать следующий код Python с помощью API Python C?

Код: Выделить всё

class Sequence():
def __init__(self, max):
self.max = max
def data(self):
i = 0
while i < self.max:
yield i
i += 1
Пока у меня есть это:

Код: Выделить всё

#include
#include 

/* Define a new object class, Sequence. */
typedef struct {
PyObject_HEAD
size_t max;
} SequenceObject;

/* Instance variables */
static PyMemberDef Sequence_members[] = {
{"max", T_UINT, offsetof(SequenceObject, max), 0, NULL},
{NULL} /* Sentinel */
};

static int Sequence_Init(SequenceObject *self, PyObject *args, PyObject *kwds)
{
if (!PyArg_ParseTuple(args, "k", &(self->max))) {
return -1;
}
return 0;
}

static PyObject *Sequence_data(SequenceObject *self, PyObject *args);

/* Methods */
static PyMethodDef Sequence_methods[] = {
{"data", (PyCFunction)Sequence_data, METH_NOARGS,
"sequence.data() -> iterator object\n"
"Returns iterator of range [0, sequence.max)."},
{NULL} /* Sentinel */
};

/* Define new object type */
PyTypeObject Sequence_Type = {
PyObject_HEAD_INIT(NULL)
0,                         /* ob_size */
"Sequence",                /* tp_name */
sizeof(SequenceObject),    /* tp_basicsize */
0,                         /* tp_itemsize */
0,                         /* tp_dealloc */
0,                         /* tp_print */
0,                         /* tp_getattr */
0,                         /* tp_setattr */
0,                         /* tp_compare */
0,                         /* tp_repr */
0,                         /* tp_as_number */
0,                         /* tp_as_sequence */
0,                         /* tp_as_mapping */
0,                         /* tp_hash */
0,                         /* tp_call */
0,                         /* tp_str */
0,                         /* tp_getattro */
0,                         /* tp_setattro */
0,                         /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags*/
"Test generator object",   /* tp_doc */
0,                         /* tp_traverse */
0,                         /* tp_clear */
0,                         /* tp_richcompare */
0,                         /* tp_weaklistoffset */
0,                         /* tp_iter */
0,                         /* tp_iternext */
0,                         /* tp_methods */
Sequence_members,          /* tp_members */
0,                         /* tp_getset */
0,                         /* tp_base */
0,                         /* tp_dict */
0,                         /* tp_descr_get */
0,                         /* tp_descr_set */
0,                         /* tp_dictoffset */
(initproc)Sequence_init,   /* tp_init */
0,                         /* tp_alloc */
PyType_GenericNew,         /* tp_new */
};

static PyObject *Sequence_data(SequenceObject *self, PyObject *args)
{
/* Now what? */
}
Но я не уверен, куда идти дальше. Может ли кто-нибудь предложить какие-нибудь предложения?
Изменить
Я полагаю, что основная проблема, с которой я сталкиваюсь, - это имитация оператора урожайности. Насколько я понимаю, это довольно простой на вид, но на самом деле сложный оператор — он создает генератор со своими собственными методами __iter__() и next(), которые вызываются автоматически. При поиске в документации кажется, что он связан с PyGenObject; однако неясно, как создать новый экземпляр этого объекта. PyGen_New() принимает в качестве аргумента PyFrameObject, единственная ссылка, на которую я могу найти, - это PyEval_GetFrame(), что, похоже, не то, что мне нужно (или я ошибаюсь ?). Есть ли у кого-нибудь опыт работы с этим, которым он может поделиться?
Дальнейшее редактирование
Я обнаружил, что это стало яснее, когда я (по сути) расширил то, что такое Python. делаем за кулисами:

Код: Выделить всё

class IterObject():
def __init__(self, max):
self.max = max
def __iter__(self):
self.i = 0
return self
def next(self):
if self.i >= self.max:
raise StopIteration
self.i += 1
return self.i

class Sequence():
def __init__(self, max):
self.max = max
def data(self):
return IterObject(self.max)
Технически последовательность отличается на единицу, но суть вы поняли.
Единственная проблема в том, что каждый раз создавать новый объект очень раздражает. раз нужен генератор — особенно в Python, чем в C, из-за необходимой чудовищности, связанной с определением нового типа. И в C не может быть оператора yield, потому что в C нет замыканий. Вместо этого я (поскольку я не смог найти его в API Python — пожалуйста укажите мне стандартный объект, если он уже существует!) — создал простой универсальный класс объекта-генератора, который вызывал обратный вызов Функция C для каждого вызова метода next(). Вот оно (обратите внимание, что я еще не пробовал его скомпилировать, поскольку оно неполное — см. ниже):

Код: Выделить всё

#include
#include 
#include 

/* A convenient, generic generator object. */

typedef PyObject *(*callback)(PyObject *callee, void *info) PyGeneratorCallback;

typedef struct {
PyObject HEAD
PyGeneratorCallback callback;
PyObject *callee;
void *callbackInfo; /* info to be passed along to callback function. */
bool freeInfo; /* true if |callbackInfo| should be free'()d when object
* dealloc's, false if not. */
} GeneratorObject;

static PyObject *Generator_iter(PyObject *self, PyObject *args)
{
Py_INCREF(self);
return self;
}

static PyObject *Generator_next(PyObject *self, PyObject *args)
{
return self->callback(self->callee, self->callbackInfo);
}

static PyMethodDef Generator_methods[] = {
{"__iter__", (PyCFunction)Generator_iter, METH_NOARGS, NULL},
{"next", (PyCFunction)Generator_next, METH_NOARGS, NULL},
{NULL} /* Sentinel */
};

static void Generator_dealloc(GenericEventObject *self)
{
if (self->freeInfo && self->callbackInfo != NULL) {
free(self->callbackInfo);
}
self->ob_type->tp_free((PyObject *)self);
}

PyTypeObject Generator_Type = {
PyObject_HEAD_INIT(NULL)
0,                         /* ob_size */
"Generator",               /* tp_name */
sizeof(GeneratorObject),   /* tp_basicsize */
0,                         /* tp_itemsize */
Generator_dealloc,         /* tp_dealloc */
0,                         /* tp_print */
0,                         /* tp_getattr */
0,                         /* tp_setattr */
0,                         /* tp_compare */
0,                         /* tp_repr */
0,                         /* tp_as_number */
0,                         /* tp_as_sequence */
0,                         /* tp_as_mapping */
0,                         /* tp_hash */
0,                         /* tp_call */
0,                         /* tp_str */
0,                         /* tp_getattro */
0,                         /* tp_setattro */
0,                         /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags*/
0,                         /* tp_doc */
0,                         /* tp_traverse */
0,                         /* tp_clear */
0,                         /* tp_richcompare */
0,                         /* tp_weaklistoffset */
0,                         /* tp_iter */
0,                         /* tp_iternext */
0,                         /* tp_methods */
0,                         /* tp_members */
0,                         /* tp_getset */
0,                         /* tp_base */
0,                         /* tp_dict */
0,                         /* tp_descr_get */
0,                         /* tp_descr_set */
0,                         /* tp_dictoffset */
0,                         /* tp_init */
0,                         /* tp_alloc */
PyType_GenericNew,         /* tp_new */
};

/* Returns a new generator object with the given callback function
* and arguments.  */
PyObject *Generator_New(PyObject *callee, void *info,
bool freeInfo, PyGeneratorCallback callback)
{
GeneratorObject *generator = (GeneratorObject *)_PyObject_New(&Generator_Type);
if (generator == NULL) return NULL;

generator->callee = callee;
generator->info = info;
generator->callback = callback;
self->freeInfo = freeInfo;

return (PyObject *)generator;
}

/* End of Generator definition. */

/* Define a new object class, Sequence. */
typedef struct {
PyObject_HEAD
size_t max;
} SequenceObject;

/* Instance variables */
static PyMemberDef Sequence_members[] = {
{"max", T_UINT, offsetof(SequenceObject, max), 0, NULL},
{NULL} /* Sentinel */
}

static int Sequence_Init(SequenceObject *self, PyObject *args, PyObject *kwds)
{
if (!PyArg_ParseTuple(args, "k", &self->max)) {
return -1;
}
return 0;
}

static PyObject *Sequence_data(SequenceObject *self, PyObject *args);

/* Methods */
static PyMethodDef Sequence_methods[] = {
{"data", (PyCFunction)Sequence_data, METH_NOARGS,
"sequence.data() -> iterator object\n"
"Returns generator of range [0, sequence.max)."},
{NULL} /* Sentinel */
};

/* Define new object type */
PyTypeObject Sequence_Type = {
PyObject_HEAD_INIT(NULL)
0,                         /* ob_size */
"Sequence",                /* tp_name */
sizeof(SequenceObject),    /* tp_basicsize */
0,                         /* tp_itemsize */
0,                         /* tp_dealloc */
0,                         /* tp_print */
0,                         /* tp_getattr */
0,                         /* tp_setattr */
0,                         /* tp_compare */
0,                         /* tp_repr */
0,                         /* tp_as_number */
0,                         /* tp_as_sequence */
0,                         /* tp_as_mapping */
0,                         /* tp_hash */
0,                         /* tp_call */
0,                         /* tp_str */
0,                         /* tp_getattro */
0,                         /* tp_setattro */
0,                         /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags*/
"Test generator object",   /* tp_doc */
0,                         /* tp_traverse */
0,                         /* tp_clear */
0,                         /* tp_richcompare */
0,                         /* tp_weaklistoffset */
0,                         /* tp_iter */
0,                         /* tp_iternext */
0,                         /* tp_methods */
Sequence_members,          /* tp_members */
0,                         /* tp_getset */
0,                         /* tp_base */
0,                         /* tp_dict */
0,                         /* tp_descr_get */
0,                         /* tp_descr_set */
0,                         /* tp_dictoffset */
(initproc)Sequence_init,   /* tp_init */
0,                         /* tp_alloc */
PyType_GenericNew,         /* tp_new */
};

static PyObject *Sequence_data(SequenceObject *self, PyObject *args)
{
size_t *info = malloc(sizeof(size_t));
if (info == NULL) return NULL;
*info = 0;

/* |info| will be free'()d by the returned generator object. */
GeneratorObject *ret = Generator_New(self, info, true,
&Sequence_data_next_callback);
if (ret == NULL) {
free(info); /* Watch out for memory leaks! */
}
return ret;
}

PyObject *Sequence_data_next_callback(PyObject *self, void *info)
{
size_t i = info;
if (i > self->max) {
return NULL; /* TODO: How do I raise StopIteration here? I can't seem to find
*       a standard exception. */
} else {
return Py_BuildValue("k", i++);
}
}
Однако, к сожалению, я еще не закончил. Единственный вопрос, который у меня остался: как мне вызвать исключение StopIteration с помощью C API? Кажется, я не могу найти его в списке стандартных исключений. И, что, возможно, более важно, правильный ли это подход к этой проблеме?
Спасибо всем, кто до сих пор следит за этим.

Подробнее здесь: https://stackoverflow.com/questions/181 ... thon-c-api
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Python»