J'ai un certain nombre de fonctions C, et je voudrais les appeler à partir de python. cython semble être la voie à suivre, mais je ne peux pas vraiment trouver un exemple de la façon exacte dont cela est fait. Ma fonction C ressemble à ceci:
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 ) ;
Tout ce que je veux faire, c'est spécifier les trois premiers paramètres (une chaîne et deux entiers), et récupérer 8 tableaux numpy (ou python listes. Tous les tableaux doubles ont N éléments). Mon code suppose que les pointeurs pointent vers un morceau de mémoire déjà alloué. De plus, le code C produit doit être lié à certaines bibliothèques externes.
Voici un exemple minuscule mais complet de passage de tableaux numpy à une fonction C externe, logiquement
fc( int N, double* a, double* b, double* z ) # z = a + b
en utilisant Cython. (Ceci est sûrement bien connu de ceux qui le connaissent bien. Les commentaires sont les bienvenus. Dernier changement: 23 février 2011, pour Cython 0.14.)
Lisez d'abord ou écrémez build Cython et Cython avec NumPy .
2 étapes:
python f-setup.py build_ext --inplace
f.pyx
et fc.cpp
-> f.so
, une bibliothèque dynamiquepython test-f.py
import f
Charge f.so
; f.fpy( ... )
appelle le C fc( ... )
.python f-setup.py
Utilise distutils
pour exécuter cython, compiler et lier:cython f.pyx -> f.cpp
compile f.cpp
et fc.cpp
lien f.o fc.o
-> f.so
, une bibliothèque dynamique que python import f
va charger.
Pour les étudiants, je suggère: faites un diagramme de ces étapes, parcourez les fichiers ci-dessous, puis téléchargez-les et exécutez-les.
(distutils
est un énorme package compliqué utilisé pour créer Python packages pour la distribution et les installer. Ici, nous en utilisons juste une petite partie pour compiler et créer un lien vers f.so
. Cette étape n'a rien à voir avec Cython, mais elle peut être déroutante; de simples erreurs dans un .pyx peuvent provoquer des pages de messages d'erreur obscurs de la compilation et du lien g ++. Voir aussi doc distutils et/ou questions SO sur les distutils .)
Comme make
, setup.py
Réexécutera cython f.pyx
Et g++ -c ... f.cpp
Si f.pyx
Est plus récent que f.cpp
.
Pour nettoyer, rm -r build/
.
Une alternative à setup.py
Serait d'exécuter les étapes séparément, dans un script ou Makefile: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
.
Modifiez le wrapper cc-lib-mac
Ci-dessous pour votre plate-forme et votre installation: ce n'est pas joli, mais petit.
Pour des exemples réels d'encapsulation Cython C, regardez les fichiers .pyx dans à peu près n'importe quel SciKit .
Voir aussi: Cython pour les utilisateurs de NumPy et SO questions/tagged/cython .
Pour décompresser les fichiers suivants, coupez-collez le lot dans un gros fichier, dites cython-numpy-c-demo
, Puis sous Unix (dans un nouveau répertoire propre) exécutez 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 \
"$@" \
-o $base.so
# undefs: nm -gpv $base.so | egrep '^ *U _+[^P]'
!
# 23 Feb 2011 13:38
Le code Cython suivant de http://article.gmane.org/gmane.comp.python.cython.user/5625 ne nécessite pas de transtypages explicites et gère également les tableaux non continus:
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])
Fondamentalement, vous pouvez écrire votre fonction Cython de telle sorte qu'elle alloue les tableaux (assurez-vous que cimport numpy as np
):
cdef np.ndarray[np.double_t, ndim=1] rr = np.zeros((N,), dtype=np.double)
passez ensuite dans le .data
pointeur de chacun vers votre fonction C. Cela devrait fonctionner. Si vous n'avez pas besoin de commencer par des zéros, vous pouvez utiliser np.empty
pour une petite augmentation de vitesse.
Voir le tutoriel Cython for NumPy Users dans la documentation (l'a corrigé sur le lien correct).
Vous devriez vérifier Ctypes c'est probablement la chose la plus simple à utiliser si tout ce que vous voulez c'est une fonction.