Уведомления

Группа в Telegram: @pythonsu

#1 Май 28, 2011 22:58:10

math.beginer
От:
Зарегистрирован: 2011-03-28
Сообщения: 40
Репутация: +  0  -
Профиль   Отправить e-mail  

Списки в C API

Пытаюсь разобраться со списками, пока простой пример, получить длину списка:

static PyObject *sum_cmd(PyObject *self, PyListObject *mass){
Py_ssize_t n=0;
n=PyList_Size((PyObject*)mass);
return Py_BuildValue("i",n);
}
в результате получаю не то что нужно
>>> import mymod
>>> mymod.sum([1,2,3,4,5])
-1
Вопросы:

1. как правильно заставить работать этот пример (возращать число элементов списка)

2. нет ли где учебника по C API, подробнее чем Extending and Embedding the Python Interpreter в плане списков?

3. PyListObject переконвертируется в PyObject, а то непонятно как действова, я жду в параметрах список, а могу работать с базовым объектом согласно синтаксису Py_ssize_t PyList_Size(PyObject *list)?

P.S. Если говорить конкретнее, то мне нужно работать с массивами numpy.ndarray размерности 1,2,3 , и если есть рекомендации как эффективнее работать с ними в C API прошу высказаться.



Офлайн

#2 Май 28, 2011 23:19:17

math.beginer
От:
Зарегистрирован: 2011-03-28
Сообщения: 40
Репутация: +  0  -
Профиль   Отправить e-mail  

Списки в C API

вот этот вариант , воообще вылетает с ошибкой сегментирования

double sum_cmd(PyObject *mass){
int i, n=0;
double x,res = 0.0;
PyObject *item;
n=PyList_Size(mass);
for(i=0;i<n;++i){
item=PyList_GetItem(mass,i);
PyArg_ParseTuple(item,"d",&x);
res+=x;
}
return res;
}



Офлайн

#3 Май 29, 2011 07:24:39

doza_and
От:
Зарегистрирован: 2010-08-15
Сообщения: 4138
Репутация: +  252  -
Профиль   Отправить e-mail  

Списки в C API

А вам в какую сторону работать? embedding или extending? Обычно встраивание в том или ином виде это расширение. Я делал расширения притона с помощью ctypes, Берется указатель и размерность средствами питона и подставляются в сишную функцию. Для numpy это очень эффективный способ.

a=numpy.zeros(5)
myfunction(a.ctypes.data,5)
Извините что не написал про С API. На таком низком уровне мне вообще никогда не требовалось рботать, сомневаюсь что и вам это нужно.



Отредактировано (Май 29, 2011 07:28:32)

Офлайн

#4 Май 29, 2011 13:20:48

Андрей Светлов
От:
Зарегистрирован: 2007-05-15
Сообщения: 3137
Репутация: +  14  -
Профиль   Адрес электронной почты  

Списки в C API

Давайте по порядку. Описание функции должно быть
static PyObject *sum_cmd(PyObject *self, PyObject *args);
args - это не первый аргумент, а кортеж аргументов (tuple). Потому что в таблице функций модуля было METH_VARARGS, я предполагаю.
Первый аргумент, он же список, получается через PyArg_ParseTuple(args, “i:myfunction”, &lst);
Дальше нужно взять размер списка. Для именно списка можно использовать две функции: PySequence_Size и PyList_Size.
PySequence_Size работает с любым объектом, у которого есть __len__. PyList_Size требует именно list или его потомка. Для проверки перед вызовом список нужно проверить на PyList_Check и выбросить исключение в случае чего.
PyList_Size должен быть быстрее чем PySequence_Size. На сколько — не проверял. Для каждого конкетного случая (зависящего в том числе и от типа аргумента), значения могут быть разными.

Для numpy нужно использовать http://docs.scipy.org/doc/numpy/reference/c-api.html точно так же, как и для стандартных встроенных объектов. Замените PyList_ на PyArray_ — и дело пойдет.

И, наконец, самое главное. Вседа проверяйте, что вам возвращают функции из C API. Если была ошибка — нужно ее обработать и, как правило, выбросить исключение. Иначе будете периодически получать segmentation fault. И ссылки не забывайте считать аккуратно — тоже распространенная причина проблем.



Офлайн

#5 Май 30, 2011 10:35:05

math.beginer
От:
Зарегистрирован: 2011-03-28
Сообщения: 40
Репутация: +  0  -
Профиль   Отправить e-mail  

Списки в C API

doza_and
А вам в какую сторону работать?
Буду пробовать провести два варианта. Ваш вариант обращения к функции С выглядит более простым, и обнадеживает. Нужно на С пересвести вот эту функцию:
def matrix(bRow, bCol, out=None):
for i in range(len(bRow)):
for j in range(len(bCol)):
out[i][j]=funct(bRow[i],bCol[j])
где bCol и bRow, в простом случае, вот такая структура типа numpy.array((n,3,3))
[[[-0.240688 -0.03071  -1.119206]
[-0.366261 -0.090405 -1.130639]
[-0.320687 0.028917 -1.221789]]

[[-0.366261 -0.090405 -1.130639]
[-0.240688 -0.03071 -1.119206]
[-0.290936 -0.141951 -1.018888]]

[[-0.169737 -0.077079 -1.003905]
[-0.290936 -0.141951 -1.018888]
[-0.240688 -0.03071 -1.119206]]

...,
Как эта структура представляется внутри, так :
void matrix(const double & bRow[][][],const double & bCol[][][],double  & out[][]){
// обращение к элементу bCol[i][j][k]
}
или так
void matrix(const double & bRow[],const double & bCol[], double  & out[]){
// обращение к элементу bCol[N*i+M*j+k]
}
?



в более сложном случае, структуры bRow и bCol сложнее, и допускают либо такую размерность (n,3,3) либо (n,4,3):
[array([[-2.103271,  0.676978,  0.793785],
[-1.95313 , 0.676978, 0.681567],
[-1.95313 , 0.594438, 0.793785]]), array([[-1.95313 , 0.594438, 0.793785],
[-1.95313 , 0.451738, 1.193785],
[-2.35313 , 0.662126, 1.193785],
[-2.35313 , 0.676978, 1.158403]]),
...



Офлайн

#6 Май 30, 2011 10:36:44

math.beginer
От:
Зарегистрирован: 2011-03-28
Сообщения: 40
Репутация: +  0  -
Профиль   Отправить e-mail  

Списки в C API

Андрей Светлов
Давайте по порядку.
Спасибо, буду разбираться. Нет ли какого учебника по разработке С модулей для Питона?



Офлайн

#7 Май 30, 2011 10:47:00

math.beginer
От:
Зарегистрирован: 2011-03-28
Сообщения: 40
Репутация: +  0  -
Профиль   Отправить e-mail  

Списки в C API

а в С++ не будет ли слишком тормозными такие структуры

std::vector<std::vector<double> > out(n,std::vector<double>(n));

std::vector<std::vector<std::vector<double> > >bRow
?

памяти хватает.



Офлайн

#8 Май 30, 2011 12:16:02

Андрей Светлов
От:
Зарегистрирован: 2007-05-15
Сообщения: 3137
Репутация: +  14  -
Профиль   Адрес электронной почты  

Списки в C API

Учебника, да еще и на русском — думаю, что не существует.

Дело не в том, тормозной std::vector или нет. Вопрос иной — как представлен numpy.array? Не нужно переконвертировать из этого массива туда и обратно на каждом шагу — берите то что уже есть. И определитесь, у вас С или таки С++? В данном случае есть разница. А вообще doza_and правильно пишет — если вам получится обойтись только ctypes — то так и делайте. Python C API, как и более высокоуровневые обертки, требует некоторого привыкания.



Офлайн

#9 Май 30, 2011 17:15:22

math.beginer
От:
Зарегистрирован: 2011-03-28
Сообщения: 40
Репутация: +  0  -
Профиль   Отправить e-mail  

Списки в C API

Андрей Светлов
Учебника, да еще и на русском — думаю, что не существует.

Дело не в том, тормозной std::vector или нет. Вопрос иной — как представлен numpy.array? Не нужно переконвертировать из этого массива туда и обратно на каждом шагу — берите то что уже есть. И определитесь, у вас С или таки С++? В данном случае есть разница. А вообще doza_and правильно пишет — если вам получится обойтись только ctypes — то так и делайте. Python C API, как и более высокоуровневые обертки, требует некоторого привыкания.
Помогите, пожайлуста, разобраться. Вычитал что std::vector поддерживает указатели и в качестве параметра функции может быть передан простой массив. Ну, и как я понял, вызывая vect.ctypes.data мы передаем указатель на начало вектора, т.е типы в коде ниже совместимы.
$ cat test.c 
#include <vector>

extern int myfill(std::vector<double> & x){
size_t n = x.size();
for(int i=0; i<n; ++i){
x[i]=i;
}
return 0;
}

$ cat test.py
#!/usr/bin/python

import numpy as N
import ctypes as C
_foo = N.ctypeslib.load_library('test', '.')
from numpy.ctypeslib import ndpointer
#_foo.myfill.argtype=ndpointer(ndim=1)
#vect=N.zeros(5,ndpointer(ndim=1))
vect=N.zeros(10)
_foo.myfill(vect.ctypes.data)
#_foo.myfill(x.ctypes.data_as(ndpointer(ndim=1)))
print vect

$ c++ -fPIC -shared test.c -o test.so

$ ./test.py
Traceback (most recent call last):
File "./test.py", line 10, in <module>
_foo.myfill(vect.ctypes.data)
File "/usr/lib/python2.7/ctypes/__init__.py", line 366, in __getattr__
func = self.__getitem__(name)
File "/usr/lib/python2.7/ctypes/__init__.py", line 371, in __getitem__
func = self._FuncPtr((name_or_ordinal, self))
AttributeError: /media/disk/call-c/test/test.so: undefined symbol: myfill



Офлайн

#10 Май 30, 2011 21:14:21

doza_and
От:
Зарегистрирован: 2010-08-15
Сообщения: 4138
Репутация: +  252  -
Профиль   Отправить e-mail  

Списки в C API

Если вас интересует быстродействие то думаю надо начать с того где определена funct - ваша функция преобразования. Пробегание по массивам будет ерундой по сравнению с вызовами питоновской функции.
Ваше затруднение с вызовом функции не ясно - вы не привели сишную функцию (или ее декларацию).

Ваша затея имеет смысл если набор функций funct тоже откомпилированы

void cCommonTensorProduct(const double* arg1,const int* shape1,int ndim1,const double* arg2,const int* shape2,int ndim2,double* result,void (*f)(double,double))
{
int index1[10],index2[10]
level=0
beg:
for(index1[level]=0;index1[level]<shape1[level];++index1[level])
{
if(level<ndim1) {++level;goto beg;}
for(;;)
{
....
result[i]=f(arg1[i],arg2[k]);
....
}
end:;
}
if(level!=0) {--level;goto end;}

}
те принимаем только указатели на данные распределенные в питоне

Со стороны питона заботимся чтобы все было правильно
def CommonTensorProduct(arg1,arg2,f):
shape1=nd.array(arg1.shape,dtype='i')
shape2=nd.array(arg2.shape,dtype='i')
res=nd.zeros(arg1.shape+arg2.shape,dtype='d')
# надо еще проверить dtype для arg1,arg2 и если не подходит - преобразовать
cCommonTensorProduct(arg1.ctypes.data,shape1.ctypes.data,len(arg1.shape),arg2.ctypes.data,shape2.ctypes.data,len(arg2.shape),res.ctypes.data,f)
std::vector вам будет только мешать в преобразованиях туда сюда. Тут лучше поучиться правильно считать смещения.
Рекомендую собрать отладочную версию test.so и при передаче в нее управления проверить насколько правильно пришли в нее аргументы. В некоторых случаях питон сам разберется если вы ошиблись, но не всегда.



Отредактировано (Май 30, 2011 21:15:35)

Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version