2010-06-15 82 views
41

我有一些C函数,我想从python中调用它们。 cython似乎是要走的路,但我无法真正找到这样做的具体例子。我的C函数如下:。使用cython简单包装C代码

void calculate_daily (char *db_name, int grid_id, int year, 
         double *dtmp, double *dtmn, double *dtmx, 
         double *dprec, double *ddtr, double *dayl, 
         double *dpet, double *dpar) ; 

所有我想要做的就是指定前三个参数(字符串和两个整数),并恢复8个numpy的阵列(或Python列表所有双阵列有N个元件)。我的代码假设指针指向已分配的内存块。另外,生成的C代码应该链接到一些外部库。

+0

我最近刚刚结束使用用Cython我的C库,你可能要采取看一下如何做到这一点的例子。我在这里详细解释了整个过程,包括构建和分发模块:http://martinsosic.com/development/2016/02/08/wrapping-c-library-as-python-module.html。 – Martinsos 2017-02-21 10:02:09

回答

63

这里的传递numpy的阵列 到外部的C函数的一个微小的,但完整的示例,逻辑

fc(int N, double* a, double* b, double* z) # z = a + b 

使用用Cython。 (这无疑是众所周知的那些谁知道它好 评论,欢迎 最近更新:。2011年2月23日,对于用Cython 0.14)

首先阅读或浏览 Cython buildCython with NumPy

2步骤:

  • python f-setup.py build_ext --inplace
    接通f.pyxfc.cpp - >f.so,动态库
  • python test-f.py
    import f负载f.so; f.fpy(...)调用C fc(...)

python f-setup.py使用distutils运行用Cython,编译和链接:
cython f.pyx -> f.cpp
编译f.cppfc.cpp
链路f.o fc.o - >f.so, 动态LIB该蟒import f将加载。

对于学生,我会建议:制作这些步骤的图表, 查看下面的文件,然后下载并运行它们。

distutils是用来 使Python包分配一个巨大的,令人费解的包,并安装它们。 这里,我们使用的只是一小部分来编译和链接f.so。 这一步无关确实与用Cython,但它可以被混淆;在.pyx 简单的错误可以从G ++编译和链接引起晦涩的错误消息的网页 参见 distutils doc 和/或 SO questions on distutils

makesetup.py将重新运行 cython f.pyxg++ -c ... f.cpp 如果f.pyxf.cpp更新。
清理,rm -r build/

setup.py另一种方法是单独运行的步骤,在脚本或生成文件:
cython --cplus f.pyx -> f.cpp # see cython -h
g++ -c ... f.cpp -> f.o
g++ -c ... fc.cpp -> fc.o
cc-lib f.o fc.o -> dynamic library f.so
修改您的平台和安装下面的cc-lib-mac包装 :它不漂亮,但很小。

有关Cython环绕C的真实示例, 请查看几乎任何 SciKit中的.pyx文件。

另请参阅: Cython for NumPy usersSO questions/tagged/cython


要解压缩下列文件, 切割粘贴很多一个大文件,说cython-numpy-c-demo, 然后在UNIX(在一个干净的新目录)运行sh cython-numpy-c-demo

#-------------------------------------------------------------------------------- 
cat >f.pyx <<\! 
# f.pyx: numpy arrays -> extern from "fc.h" 
# 3 steps: 
# cython f.pyx -> f.c 
# link: python f-setup.py build_ext --inplace -> f.so, a dynamic library 
# py test-f.py: import f gets f.so, f.fpy below calls fc() 

import numpy as np 
cimport numpy as np 

cdef extern from "fc.h": 
    int fc(int N, double* a, double* b, double* z) # z = a + b 

def fpy(N, 
    np.ndarray[np.double_t,ndim=1] A, 
    np.ndarray[np.double_t,ndim=1] B, 
    np.ndarray[np.double_t,ndim=1] Z): 
    """ wrap np arrays to fc(a.data ...) """ 
    assert N <= len(A) == len(B) == len(Z) 
    fcret = fc(N, <double*> A.data, <double*> B.data, <double*> Z.data) 
     # fcret = fc(N, A.data, B.data, Z.data) grr char* 
    return fcret 

! 

#-------------------------------------------------------------------------------- 
cat >fc.h <<\! 
// fc.h: numpy arrays from cython , double* 

int fc(int N, const double a[], const double b[], double z[]); 
! 

#-------------------------------------------------------------------------------- 
cat >fc.cpp <<\! 
// fc.cpp: z = a + b, numpy arrays from cython 

#include "fc.h" 
#include <stdio.h> 

int fc(int N, const double a[], const double b[], double z[]) 
{ 
    printf("fc: N=%d a[0]=%f b[0]=%f \n", N, a[0], b[0]); 
    for(int j = 0; j < N; j ++){ 
     z[j] = a[j] + b[j]; 
    } 
    return N; 
} 
! 

#-------------------------------------------------------------------------------- 
cat >f-setup.py <<\! 
# python f-setup.py build_ext --inplace 
# cython f.pyx -> f.cpp 
# g++ -c f.cpp -> f.o 
# g++ -c fc.cpp -> fc.o 
# link f.o fc.o -> f.so 

# distutils uses the Makefile distutils.sysconfig.get_makefile_filename() 
# for compiling and linking: a sea of options. 

# http://docs.python.org/distutils/introduction.html 
# http://docs.python.org/distutils/apiref.html 20 pages ... 
# https://stackoverflow.com/questions/tagged/distutils+python 

import numpy 
from distutils.core import setup 
from distutils.extension import Extension 
from Cython.Distutils import build_ext 
# from Cython.Build import cythonize 

ext_modules = [Extension(
    name="f", 
    sources=["f.pyx", "fc.cpp"], 
     # extra_objects=["fc.o"], # if you compile fc.cpp separately 
    include_dirs = [numpy.get_include()], # .../site-packages/numpy/core/include 
    language="c++", 
     # libraries= 
     # extra_compile_args = "...".split(), 
     # extra_link_args = "...".split() 
    )] 

setup(
    name = 'f', 
    cmdclass = {'build_ext': build_ext}, 
    ext_modules = ext_modules, 
     # ext_modules = cythonize(ext_modules) ? not in 0.14.1 
    # version= 
    # description= 
    # author= 
    # author_email= 
    ) 

# test: import f 
! 

#-------------------------------------------------------------------------------- 
cat >test-f.py <<\! 
#!/usr/bin/env python 
# test-f.py 

import numpy as np 
import f # loads f.so from cc-lib: f.pyx -> f.c + fc.o -> f.so 

N = 3 
a = np.arange(N, dtype=np.float64) 
b = np.arange(N, dtype=np.float64) 
z = np.ones(N, dtype=np.float64) * np.NaN 

fret = f.fpy(N, a, b, z) 
print "fpy -> fc z:", z 

! 

#-------------------------------------------------------------------------------- 
cat >cc-lib-mac <<\! 
#!/bin/sh 
me=${0##*/} 
case $1 in 
"") 
    set -- f.cpp fc.cpp ;; # default: g++ these 
-h* | --h*) 
    echo " 
$me [g++ flags] xx.c yy.cpp zz.o ... 
    compiles .c .cpp .o files to a dynamic lib xx.so 
" 
    exit 1 
esac 

# Logically this is simple, compile and link, 
# but platform-dependent, layers upon layers, gloom, doom 

base=${1%.c*} 
base=${base%.o} 
set -x 

g++ -dynamic -arch ppc \ 
    -bundle -undefined dynamic_lookup \ 
    -fno-strict-aliasing -fPIC -fno-common -DNDEBUG `# -g` -fwrapv \ 
    -isysroot /Developer/SDKs/MacOSX10.4u.sdk \ 
    -I/Library/Frameworks/Python.framework/Versions/2.6/include/python2.6 \ 
    -I${Pysite?}/numpy/core/include \ 
    -O2 -Wall \ 
    "[email protected]" \ 
    -o $base.so 

# undefs: nm -gpv $base.so | egrep '^ *U _+[^P]' 
! 

# 23 Feb 2011 13:38 
+1

实际上没有必要使用带'char *'指针的包装函数。你可以直接在Cython中包装'fcreal()',并将其命名为'fcret = fcreal(N,A.data,B.data, Z.data)''。另外,它容易出错并且不能移植到单独编译'fc.o'的地方。只需在'sources ='中包含'fc.cpp'即可。 – oceanhug 2010-12-12 06:55:28

+1

如果传递的numpy数组在内存中不连续或者Fortran字节顺序,这可能会产生意想不到的结果。另外,所需演员阵容有点令人讨厌。请参阅下面的更好的cython代码。 – Nikratio 2012-02-02 17:10:47

+0

@denis我有一个[Cython的帖子](http://stackoverflow.com/questions/41944883/verifying-compatibility-in-compiling-extension-types-and-using-them-with-cdef)你可能能够提供洞察力。 – Phillip 2017-01-31 20:58:29

2

你应该检查出Ctypes如果你想要的只是一个功能,它可能是最容易使用的东西。

+0

是的,但我想用cython后来包装其他的东西,所以这是我的出发点:) – Jose 2010-06-15 15:41:04

+3

即使小包装使用ctypes是危险和脆弱的,因为这种一般方法的缺陷(不使用头文件等向前)。 – 2011-02-28 19:11:11

+0

问题问Cython;这个问题没有任何回应。 – 2016-06-13 18:47:41

3

基本上,你可以写你用Cython功能,使得它分配阵列(确保你cimport numpy as np):

cdef np.ndarray[np.double_t, ndim=1] rr = np.zeros((N,), dtype=np.double) 

然后传中,.data指针每到C的功能。这应该工作。如果你不需要从零开始,你可以使用np.empty进行小幅度的提升。

请参阅文档中的Cython for NumPy Users教程(将其固定到正确的链接)。

12

http://article.gmane.org/gmane.comp.python.cython.user/5625下面用Cython代码并不需要显式转换,也处理非连续数组:

def fpy(A): 
    cdef np.ndarray[np.double_t, ndim=2, mode="c"] A_c 
    A_c = np.ascontiguousarray(A, dtype=np.double) 
    fc(&A_c[0,0]) 
+0

这是否会导致额外的内存副本(如果数组已经是连续的)? – dashesy 2014-01-03 02:58:53

+0

@dashesy:不,如果数组已经是连续的,就没有额外的副本。 – Nikratio 2014-01-03 17:24:11

+0

如果A_c是1-D,是否以fc(&A_c [0])的形式传递是正确的? – ascetic652 2017-05-31 19:16:26