De nombreux articles ont résumé comment appeler une fonction écrite en C à partir de Python, mais j'y étais principalement accro car il y avait peu d'articles qui décrivaient en détail comment appeler une fonction de C vers Python. ・ Préparez un mémo.
Une erreur se produit lorsque quelqu'un tente de référencer un argument de ligne de commande. Si vous êtes pris dans l'erreur ci-dessus, définissez-la.
pytest.py
import sys
if not hasattr(sys, 'argv'):
sys.argv = [''] #Ajoutez des arguments de ligne de commande ici si nécessaire
En C, les parties qui sont normalement utilisées sans en avoir connaissance, comme la gestion des compteurs de référence, sont manuscrites.
callPy.c
#include <Python.h>
//Ajouter le répertoire actuel au chemin
void set_path() {
PyObject *sys = PyImport_ImportModule("sys");
PyObject *sys_path = PyObject_GetAttrString(sys, "path");
PyList_Append(sys_path, PyUnicode_DecodeFSDefault("."));
}
void main(){
PyObject *pModule, *pFunc, *pName;
Py_Initialize();
set_path();
pName = PyUnicode_DecodeFSDefault("pytest");//nom de fichier
pModule = PyImport_Import(pName);
Py_DECREF(pName);//Les références qui sont utilisées décrémentent le nombre de références.
if(pModule != NULL){
pFunc = PyObject_GetAttrString(pModule, "pyMethod");//Nom de la fonction
}
}
Pour une raison quelconque, si vous placez le fichier Python du module à côté du fichier exécutable, il ne peut pas être lu et il semble que vous deviez ajouter le dossier actuel au chemin.
callpy.c
typedef struct{
int val1;
float val2;
float val3;
float val4;
} STRUCT2;
#define ARRAY_MAX 100
typedef struct{
int structNo;
STRUCT2 results[ARRAY_MAX];
} STRUCT1;
Si vous souhaitez définir une structure C comme celle ci-dessus côté Python, En Python, il est défini à l'aide de ctypes.
pytest.py
class STRUCT2(Structure):
_fields_ = [
('val1', c_int32),
('val2', c_float),
('val3', c_float),
('val4', c_float),
]
class STRUCT1(Structure):
ARRAY_MAX = 100
_fields_ = [
('dataNo', c_int32),
('dataArray', STRUCT2 * ARRAY_MAX)
]
S'il s'agit d'un tableau de pointeurs au lieu d'une entité de tableau, il peut être défini comme un pointeur comme indiqué ci-dessous.
POINTER(STRUCT2) * ARRAY_MAX
Je ne suis pas satisfait d'avoir à définir la longueur du tableau comme valeur initiale même s'il s'agit de Python. Existe-t-il un moyen de le définir dynamiquement lors d'un appel depuis C?
Ajoutez PyObject à Tuple dans la liste d'arguments et appelez la fonction avec CallObject
callpy.c
PyObject *pValue; //Pointeur pour la valeur de retour
Pyobject *pArgArray[3];//Prenez 3 arguments
Pyobject *pArgs = PyTuple_New(3);//Liste d'arguments
//Convertir la valeur de chaque argument en type PyObject
pArg[0] = PyUnicode_FromString("hogehoge");//Chaîne
pArg[1] = PyLong_FromLong(1000);//Valeur numérique,int
pArg[2] = PyFloat_FromDouble(0.998);//Fraction flottante
for(int i = 0; i < 3 ; i++){
PyTuple_SetItem(pArgs, i, pArg[i]);//Définir des arguments dans la liste d'arguments
}
pValue = PyObject_CallObject(pFunc, pArgs);
Par exemple, si vous souhaitez transmettre une image brute RVB 640 * 480, utilisez PyArray_SimpleNewFromData pour en faire un objet de type tableau 3D.
callpy.c
import_array();
npy_intp dims[3] = {480, 640, 3};
pArg = PyArray_SimpleNewFromData(3, dimensions, NPY_UINT8, image);
S'il s'agit d'une valeur de retour de type PyObject, elle peut être retournée de la même manière qu'une fonction Python. Bien sûr, plusieurs valeurs de retour sont OK
pytest.py
return val1, val2 #int, string
callpy.c
PyObject* pValue;
int num;
float decimal;
---Abréviation---
pValue = PyObject_CallObject(pFunc, pArgs);
int result = PyArg_ParseTuple(pValue, "is", &num, &decimal);
Il existe plusieurs façons de recevoir la valeur de retour en C, mais ParseTuple semble être bon pour le moment. Confirmer le format du type de retour à l'avance.
S'il s'agissait d'un type ctypes comme C, j'étais accro aux essais et erreurs en pensant que je devrais simplement passer le pointeur de Python à C.
pytest.py
memoryview(mystruct).tobytes()
Quand j'ai créé un tableau d'octets comme celui-ci, il semble qu'il soit nécessaire d'en faire un type PyObject une fois car c'est un type de tableau de ctypes, et il ne peut pas être passé comme un pointeur C tel quel. Cependant, même si je l'ai passé en tant que type PyObject, je ne savais pas comment l'analyser avec c, donc je l'ai implémenté en ** passant en utilisant le type numpy.darray **.
pytest.py
return np.frombuffer(struct, c_int8)
Convertissez simplement la structure directement en type darray avec numpy.frombuffer et recevez-la sous forme de tableau numpy avec C.
callpy.c
PyArrayObject *pArray;
PyArg_ParseTuple(pValue, "O", &pArray );
STRUCT1* struct_ptr = (STRUCT1*)PyArray_DATA(pArray);
Au début, je ne savais pas du tout comment transmettre d'énormes données, alors j'ai essayé diverses choses telles que l'utilisation du mappage de mémoire, mais à la fin, passer en tant que tableau d'octets avec numpy.darray semble être le plus rapide et le plus facile à comprendre. ..
Recommended Posts