Exemples de codes Python et écriture d'extensions en langage C

On trouve sur le Web des milliers d'exemples et de cours concernant le langage Python.
Mais cela m'a pris une journée pour apprendre à appeler des fonctions écrites en langage C, depuis un programme Python.
Pourquoi faire ?
Le langage Python est pratique, mais très lent dans les calculs scientifiques et pour accéder aux ports GPIO du Raspberry pi. Plus de 100 fois plus lent que le langage C, selon mon expérience et selon ce qu'on peut lire sur le Web.
Alors voici des exemples pour apprendre à étendre le code Python en fonctions du langage C.
Différentes références sont données ici, d'autres seront données sous certain exemples.

Je dois préciser que je travail sous GNU/Linux KUbuntu 16.04. J'utilise Python, version 3.
Pour d'autres systèmes d'exploitations, il faut faire des analogies.
Pour écrire et compiler mon code C, j'ai utilisé le logiciel libre Geany.
Plus d'information concernant Geany.
J'ai également fait des essais avec Code Blocks.

Il existe deux approches complètement différentes pour permettre d'appeler des fonctions écrites en langage C, par des scripts Python.
1) Soit on met la partie de code faisant la connexion dans le script Python.
  C'est ce qui est montré dans le premier exemple, avec l'utilisation de la librairie Python : ctypes
2) Soit on met la partie de code faisant la connexion dans la partie écrite en langage C.
Dans ce cas, on utilise un #include "Python.h".
C'est plus compliqué, mais ensuite, vu depuis Python, c'est plus simple à utiliser. C'est donc le développeur de la librairie C qui devra plus travailler.
Les exemples nommées "test?.c et .py" montrent cette approche.

Dans les deux cas, sous Linux, il faut avoir installé le compilateur gcc et les librairies qui vont avec.
sudo apt-get update
sudo apt-get install build-essential
sudo apt-get install python3-dev
Si la librairie a déjà été installée, les commandes ci-dessous ne ferront rien, donc on ne risque rien de les taper, en cas de doutes.

Pour les essais, je conseille d'utiliser les mêmes noms que ceux qui sont indiqués et de les stocker dans le même dossier.
Les fichiers en langage C devront être compiler ainsi :
gcc -shared -o test?.so -fPIC test?.c
Le ? doit être remplacé par le bon chiffre, c.f. les noms de fichiers.
Information sur gcc pour compiler correctement le code C.

Voici maintenant les exemples

Les noms des fichiers en langage C et en script Python sont les même ici, mais ce n'est pas nécessaire, je l'ai fait pour reconnaître que le script Python appelle le code écrit en C.
° ctypes_test1.c et .py
° ctypes_test2.c et .py
---------- FIN de méthode mettant le code dans Python ----------
° test0.c et .py
On donne une chaîne de caractères, On reçoit le nombre d'octets pris par cette chaîne.
° test1.c et .py
Un exemple, qui retourne le produit de deux nombres entiers donnés en arguments.
° test2.c et .py
Un exemple, qui retourne le produit de deux nombres à virgule donnés en arguments.
° test3.c et .py
Un exemple qui envoie de 2 arguments Long, pour récupérer un Float.
° test4.c et .py
Un exemple qui envoie de 2 arguments Long, pour récupérer un Float.
° test5.c et .py
Envoie de 2 arguments Long, pour récupérer un Float.
Il y a deux fonctions ajoutée dans cet exemple.
° test6.c et .py
Développement du test5.
° test7.c et .py
Développement du test5, assez complex, Pour résoudre un problème mathématique de convergence très lente.
° test8.c et .py
Envoie et réception de strings et de Lists (=array)
---------- FIN de méthodes mettant le code dans C ----------

ctypes_test1.c   TOP
/*
ctypes_test1.c
L'objectif est d'avoir une soubroutine en C
qui peut être accédé depuis le langage Python.

Pour compiler depuis un Terminal :
gcc -shared -Wl,-soname,ctypes_test1 -o ctypes_test1.so -fPIC ctypes_test1.c
gcc -shared -o ctypes_test1.so -fPIC ctypes_test1.c
Pour les options,
c.f. https://gcc.gnu.org/onlinedocs/gcc/Option-Summary.html

L'option "-Wl,-soname,pyco1" me semble inutile.
********************************************************/

#include <stdio.h>
#include <string.h>
// c.f. https://www.tutorialspoint.com/c_standard_library/string_h.htm

int hellotest() {
//===============
printf("Hello Wolrd\n");  // ne sera écrit nulle part !
return 7;
} // hellotest

long multiplier(long a, long b) {
//===============================
//Attend deux entiers et les multiple
return a * b;
} // multiplier

// Ne fonctionne pas, le type float et le type double ne sont pas
// reconnus.
double multiplierfxf(double a, double b) {
//========================================
//Attend deux nombres à virgule et les multiplie
return a * b;
} // multiplierfxf


char *dit_papa(char *p) {
//=======================
// Retourne le string envoyé en argument, suivit de " papa".
return strcat(p, " papa");
}


char *dit_bonjour(char *p) {
//==========================
// Retourne "Bonjour " suivit du string envoyé en argument
// c.f. https://www.tutorialspoint.com/cprogramming/c_arrays.htm
char temp[100];
strcpy(temp, "Bonjour ");
return strcat(temp, p);
}

ctypes_test1.py   un script Python qui appelle la fonction écrite en C.
# ctypes_test1.py
# L'objectif est d'accéder à des routines écrites en langage C
# depuis un programme Python.

# c.f. https://docs.python.org/3/library/ctypes.html
import ctypes
montest = ctypes.CDLL("/home/bg/Bg2/Python/ctests/testsimple.so")
# Il faut donner le chemin complet du fichier  .so  à inclure.
# Sous Linux, un fichier  .so  correspond à une  .dll  sous Windows.

print(montest.hellotest())

# Par défaut, les type sont int ou long,
# donc, il n'y a pas de conversion à faire.
a = 12
#a = ctypes.c_int(12) # a le même effet que : a = 12
b = 11
c = montest.multiplier(a, b) # Ceci fonctionne bien !
print("a*b=", a, "*", b, "=", c)
print("Le type de  c  est :", type(c))
print()
#================================================

va = 1.1
vb = 1.2
# Converti en type "double" du langage C
cva = ctypes.c_double(va)
cvb = ctypes.c_double(vb)
#print(cva) # pour information
#print(cva.value) # pour information

# Indique que le type de retour de la fonction est "double"
montest.multiplierfxf.restype = ctypes.c_double 

# Appelle de la fonction écrite en langage C
cvres = montest.multiplierfxf(cva, cvb)

print("va*vb =", cva.value, "*", vb, "=", cvres)
print("Le type de cvres est :", type(cvres))
print()
#================================================
mypystring = "le kiwi"
astr = ctypes.c_char_p(mypystring.encode('utf8'))
print(astr.value)

# Indique le type de la variable retournée par la fonction  C
montest.dit_papa.restype = ctypes.c_char_p
cstrrep = montest.dit_papa(astr)
print(cstrrep)
print(astr.value) # L'appelle à la fonction a modifié la valeur de astr.value !
print("Le type de  cstrrep  est :", type(cstrrep))
print()
#================================================

# Indique le type de la variable retournée par la fonction  C
montest.dit_bonjour.restype = ctypes.c_char_p
cret = montest.dit_bonjour(ctypes.c_char_p("Monsieur".encode('utf8')))
print(cret)
print("Le type de  cret  est :", type(cret))

# c.f. # Indique le type de la variable retournée par la fonction  C
# 16.16.1.4 Fundamental data types :
# Ce paragraphe indique les correspondances entre les type en langage C
# et ce qui est utilisable en Python.
# Il faut changer les type des arguments que l'on transmet au C
# Avec  .restype = ...  il faut indiquer le type de la variable retournée.



ctypes_test2.c   TOP
/*
ctypes_test2.c
L'objectif est d'avoir une soubroutine en C
qui peut être accédé depuis le langage Python.
 
gcc -shared -o ctypes_test2.so -fPIC ctypes_test2.c

gcc -I /usr/include/python3.5 -shared -Wl,-soname,pyco2 -o pyc02.so -fPIC pyc02.c
"-I /usr/include/python3.5" indique où trouver la librairie 
 
`gcc -print-prog-name=cc1` -v
Pour afficher la liste des répertoires utilisés par gcc
 
c.f. http://www.network-theory.co.uk/docs/gccintro/gccintro_21.html
-------------------------------------------------------------------
Pour l'emplacement des librairies utilisées par  gcc.

Par défaut, gcc cherche les headers dans :
/usr/local/include/
/usr/include/

Par défaut, gcc cherche les librairies dans :
/usr/local/lib/
/usr/lib/

On peut ajouter des répertoires de  "include" (.h) ainsi :
C_INCLUDE_PATH="/usr/include/python3.5"
export C_INCLUDE_PATH

gcc -shared -o pyc02.so -fPIC pyc02.c
Sera suffisant pour compiler le programme c, qui utilise 
une librairie de Python

Pour du C++, il faut :
CPLUS_INCLUDE_PATH=/opt/gdbm-1.8.3/include
export CPLUS_INCLUDE_PATH

Pour des librairies :
LIBRARY_PATH=/opt/gdbm-1.8.3/lib
export LIBRARY_PATH

This directory will be searched after any directories specified on the command line with the option -L, 
and before the standard default directories (such as ‘/usr/local/lib’ and ‘/usr/lib’).
c.f. : http://www.network-theory.co.uk/docs/gccintro/gccintro_23.html
******************************************************************************/

int fact(int n) {
//===============
// Fonction factorielle récursive en C
if (n < 2) return(1);
return (n)*fact(n-1);
} // fact

int pgcd(int a, int b) {
//======================
// fonction 'pgcd' en C
int r;
while (b!=0) {
  r = a%b;
  a = b;
  b = r;
  }
return a;
} // pgcd

ctypes_test2.py   un script Python qui appelle la fonction écrite en C.
# ctypes_test2.py
# L'objectif est d'accéder à des routines écrites en langage C
# depuis un programme Python.

# c.f. https://docs.python.org/3/library/ctypes.html
import ctypes
montest = ctypes.CDLL("/home/bg/Bg2/Python/ctests/ctypes_test2.so")
# Il faut donner le chemin complet du fichier  .so  à inclure.
# Sous Linux, un fichier  .so  correspond à une  .dll  sous Windows.

print(montest.fact(7))
print(montest.pgcd(5040, 3003))
# Fonctionne très bien.

a=5040
b=3003
c=montest.pgcd(a, b)
print("Le pgcd(", a, ",", b, ") =",c)

n=10
n_fact = montest.fact(n)
print(n, "! =", n_fact)

# Ces tests fonctionnent très bien et sont très simples,
# car on utilise que des nombres entiers !

---------- FIN de méthode mettant le code dans Python ----------

test0.c   TOP
/*
test0.c
L'objectif est d'avoir une soubroutine en C
qui peut être accédé depuis le langage Python.

Pour compiler depuis un Terminal :
gcc -shared -o test0.so -fPIC test0.c

Depuis Python, si on veut taper dans la console :
import os
os.getcwd() # indique le répertoire par défaut
os.chdir("/home/bg/Bg2/Python/ctests")  # pour changer le répertoire par défaut
import test0
test0.compte("abcdefgh")  # retournera la longueur du string. Ici = 8

Mieux : exécuter le script test0.py qui est donné dans un autre fichier.

Pour les types que Python reconnait et peut convertir en C :
https://www.tutorialspoint.com/python/python_further_extensions.htm

Curiosité :
import sys
sys.getrefcount(test0)
*****************************************************/

#include "Python.h"
#include <string.h>
// c.f. https://www.tutorialspoint.com/c_standard_library/string_h.htm

static PyObject* test0_compte(PyObject *self, PyObject *args) {
//=============================================================
// Définit la fonction en C qui sera appelée depuis Python
const char* command;
int sts;

if (!PyArg_ParseTuple(args, "s", &command)) return NULL;
  // c.f. https://docs.python.org/3.3/c-api/arg.html#PyArg_ParseTuple
sts = strlen(command);  // retourne la longueur de la chaine de caractères
    // Plus exactement, le nombre d'octets qui code cette chaîne de caractères
return PyLong_FromLong(sts);
} // test0_compte

static PyMethodDef test0Methods[] = {
//===================================
{"compte",  test0_compte, METH_VARARGS,
 "Compte le nombre de caractères d'une chaîne,\
 les caractères accentués comptent double.\n\
 Il faut donner une chaine de caractères en paramètre."},
 // Est un texte d'aide. Obtenu avec : print(test0.compte.__doc__)
 {NULL, NULL, 0, NULL}  // Sentinel 
};

static struct PyModuleDef test0module = {
//=======================================
// Est une structure nécessaire pour Python ???
  PyModuleDef_HEAD_INIT,
  "test0",   /* name of module */
  NULL,  //test0_doc, /* module documentation, may be NULL */
  -1,       /* size of per-interpreter state of the module,
             or -1 if the module keeps state in global variables. */
  test0Methods
};

PyMODINIT_FUNC PyInit_test0(void) {
//=================================
// Fonction de création du module
return PyModule_Create(&test0module);
} // PyInit_test0

int main(int argc, char *argv[]) {
//================================
// Fonction principale, qui sera exécutée au chargement du module
wchar_t *program = Py_DecodeLocale(argv[0], NULL);
if (program == NULL) {
  fprintf(stderr, "Fatal error: cannot decode argv[0]\n");
  exit(1);
  }

/* Add a built-in module, before Py_Initialize */
PyImport_AppendInittab("test0", PyInit_test0);

/* Pass argv[0] to the Python interpreter */
Py_SetProgramName(program);

/* Initialize the Python interpreter.  Required. */
Py_Initialize();

/* Optionally import the module; alternatively,
   import can be deferred until the embedded script
   imports it. */
PyImport_ImportModule("test0");

PyMem_RawFree(program);
return 0;
} // main

// Il y a beaucoup de points que je ne comprends pas, mais cela fonctionne !


Oui, la partie écrite en langage C est compliquée, même pour une tout petite fonction qui ne fait que de retourner la longueur d'une chaine de caractères.
Mais une fois que l'on a le modèle, il suffit de faire un "copier-coller" et quelques petites modifications.
L'essentiel du travail sera pour écrire la fonction qui ajoute la fonctionnalité désirée.

test0.py   un script Python qui appelle la fonction écrite en C.
"""
test0.py
Ceci est un exemple d'appel à une routine écrite en langage C.

Test la librairie de démo "test0.c"

"""

import test0

print(test0.compte("abcdefgh")) # affiche 8, qui est le nombre de caractères.
print(test0.compte("abc def")) # affiche 7, qui est le nombre de caractères.
print(test0.compte("aéb")) # affiche 4, car en utf-8, "é" prend 2 octets
str="Ceci est dans une variable"
print(test0.compte(str))
print(test0.compte.__doc__) # pour afficher de l'aide sur la fonction test0.compte


test1.c   TOP
/*
test1.c
L'objectif est d'avoir une soubroutine en C
qui peut être accédé depuis le langage Python.

Pour compiler depuis un Terminal :
gcc -shared -o test1.so -fPIC test1.c

Depuis Python, si on veut taper dans la console :
import os
os.getcwd() # indique le répertoire par défaut
os.chdir("/home/bg/Bg2/Python/ctests")  # pour changer le répertoire par défaut
import test1
test1.mult(11,7)  # retournera le produit 11 * 7
Les arguments doivent être deux nombres entiers.

Mieux : exécuter le script test1.py qui est donné dans un autre fichier.

Pour les types que Python reconnait et peut convertir en C :
https://www.tutorialspoint.com/python/python_further_extensions.htm

Curiosité :
import sys
sys.getrefcount(test1)
***********************************************************/

#include "Python.h"

static PyObject *test1_mult(PyObject *self, PyObject *args) {
//=============================================================
// Définit la fonction en C qui sera appelée depuis Python
const long lNb1;
const long lNb2;
long lRep;

if (!PyArg_ParseTuple(args, "ll", &lNb1, &lNb2)) return NULL;
  // c.f. https://docs.python.org/3.3/c-api/arg.html#PyArg_ParseTuple
lRep = lNb1 * lNb2;  

return PyLong_FromLong(lRep);
// c.f. https://docs.python.org/3.3/c-api/long.html
} // test1_mult

static PyMethodDef test1Methods[] = {
//===================================
{"mult",  test1_mult, METH_VARARGS,
 "mult prend deux nombres entiers en entrée et retourne leur produit."},
 // Est un texte d'aide. Obtenu avec : print(test1.mult.__doc__)
 {NULL, NULL, 0, NULL}  // Sentinel 
};

static struct PyModuleDef test1module = {
//=======================================
// Est une structure nécessaire pour Python ???
  PyModuleDef_HEAD_INIT,
  "test1",   /* name of module */
  NULL,  //test1_doc, /* module documentation, may be NULL */
  -1,       /* size of per-interpreter state of the module,
             or -1 if the module keeps state in global variables. */
  test1Methods
};

PyMODINIT_FUNC PyInit_test1(void) {
//=================================
// Fonction de création du module
return PyModule_Create(&test1module);
} // PyInit_test1

int main(int argc, char *argv[]) {
//================================
// Fonction principale, qui sera exécutée au chargement du module
wchar_t *program = Py_DecodeLocale(argv[0], NULL);
if (program == NULL) {
  fprintf(stderr, "Fatal error: cannot decode argv[0]\n");
  exit(1);
  }

/* Add a built-in module, before Py_Initialize */
PyImport_AppendInittab("test1", PyInit_test1);

/* Pass argv[0] to the Python interpreter */
Py_SetProgramName(program);

/* Initialize the Python interpreter.  Required. */
Py_Initialize();

/* Optionally import the module; alternatively,
   import can be deferred until the embedded script
   imports it. */
PyImport_ImportModule("test1");

PyMem_RawFree(program);
return 0;
} // main

// Il y a beaucoup de points que je ne comprends pas, mais cela fonctionne !

Remarquez que l'essentiel du code est le même que celui du test0.c, ci-dessus.

test1.py   un script Python qui appelle la fonction écrite en C.
"""
test1.py
Ceci est un exemple d'appel à une routine écrite en langage C.

Test la librairie de démo "test1.c"

"""

import test1

print(test1.mult(7, 8))
print(test1.mult(123456789, 123))
print(test1.mult(111111111, 111111111))
a=11
b=12
print(test1.mult(a, b))
print(test1.mult.__doc__) # pour afficher de l'aide sur la fonction.


test2.c   TOP
/*
test2.c
L'objectif est d'avoir une soubroutine en C
qui peut être accédé depuis le langage Python.

Pour compiler depuis un Terminal :
gcc -shared -o test2.so -fPIC test2.c

Depuis Python, si on veut taper dans la console :
import os
os.getcwd() # indique le répertoire par défaut
os.chdir("/home/bg/Bg2/Python/ctests")  # pour changer le répertoire par défaut
import test2
test2.mult(3.1415, 7.0)  # retournera le produit 3.1415 * 7
Les arguments doivent être deux nombres à virgule.

Mieux : exécuter le script test2.py qui est donné dans un autre fichier.

Pour les types que Python reconnait et peut convertir en C :
https://www.tutorialspoint.com/python/python_further_extensions.htm

Curiosité :
import sys
sys.getrefcount(test2)
**************************************************************/

#include "Python.h"

static PyObject *test2_mult(PyObject *self, PyObject *args) {
//=============================================================
// Définit la fonction en C qui sera appelée depuis Python
const double vNb1;
const double vNb2;
double vRep;

if (!PyArg_ParseTuple(args, "dd", &vNb1, &vNb2)) return NULL;
vRep = vNb1 * vNb2;  

return PyFloat_FromDouble(vRep);
// https://docs.python.org/3.3/c-api/float.html
} // test2_mult

// Définit l'aide associée à la fonction.
#define AIDE "mult prend deux nombres à virgule en \
entrée et retourne leur produit."

static PyMethodDef test2Methods[] = {
//===================================
{"mult",  test2_mult, METH_VARARGS,
  AIDE}, // Est un texte d'aide. Obtenu avec : print(test2.mult.__doc__)
 {NULL, NULL, 0, NULL}  // Sentinel 
};

static struct PyModuleDef test2module = {
//=======================================
// Est une structure nécessaire pour Python ???
  PyModuleDef_HEAD_INIT,
  "test2",   /* name of module */
  NULL,  //test2_doc, /* module documentation, may be NULL */
  -1,       /* size of per-interpreter state of the module,
             or -1 if the module keeps state in global variables. */
  test2Methods
};

PyMODINIT_FUNC PyInit_test2(void) {
//=================================
// Fonction de création du module
return PyModule_Create(&test2module);
} // PyInit_test2

int main(int argc, char *argv[]) {
//================================
// Fonction principale, qui sera exécutée au chargement du module
wchar_t *program = Py_DecodeLocale(argv[0], NULL);
if (program == NULL) {
  fprintf(stderr, "Fatal error: cannot decode argv[0]\n");
  exit(1);
  }

/* Add a built-in module, before Py_Initialize */
PyImport_AppendInittab("test2", PyInit_test2);

/* Pass argv[0] to the Python interpreter */
Py_SetProgramName(program);

/* Initialize the Python interpreter.  Required. */
Py_Initialize();

/* Optionally import the module; alternatively,
   import can be deferred until the embedded script
   imports it. */
PyImport_ImportModule("test2");

PyMem_RawFree(program);
return 0;
} // main

// Il y a beaucoup de points que je ne comprends pas, mais cela fonctionne !

test2.py   un script Python qui appelle la fonction écrite en C.
"""
test2.py
Ceci est un exemple d'appel à une routine écrite en langage C.

Test la librairie de démo "test2.c"
"""

import test2

print(test2.mult(3.1415, 7.0))
print(test2.mult(3.141593, 113.0)) 
print(test2.mult(1.111e8, 1.111e9))
a = 9.999
b = 1.111
print(test2.mult(a, b))
print(test2.mult.__doc__) # pour afficher de l'aide sur la fonction.

test3.c   TOP
/*
test3.c
L'objectif est d'avoir une soubroutine en C
qui peut être accédé depuis le langage Python.

Pour compiler depuis un Terminal :
gcc -shared -o test3.so -fPIC test3.c

Depuis Python, si on veut taper dans la console :
import os
os.getcwd() # indique le répertoire par défaut
os.chdir("/home/bg/Bg2/Python/ctests")  # pour changer le répertoire par défaut
import test3
test3.sommeInv(700)  # retournera la somme des inverses des entiers de 1 à 700
L'argument doit être un nombre entier.
Retourne un nombre à virgule

Mieux : exécuter le script test3.py qui est donné dans un autre fichier.

Pour les types que Python reconnait et peut convertir en C :
https://www.tutorialspoint.com/python/python_further_extensions.htm
***********************************************************************/

#include "Python.h"

static PyObject *test3_sommeInv(PyObject *self, PyObject *args) {
//===============================================================
// Définit la fonction en C qui sera appelée depuis Python
// PyObject *x_obj;  // autre possibilité.
// Somme des inverses des nombres entiers de lNbMin à lNbMax-1
// lNbMin et lNbMax sont les deux arguments.
long lNbMax;
long lNbMin;
long lNbr;
double vRep;

if (!PyArg_ParseTuple(args, "ll", &lNbMin, &lNbMax)) return NULL; 
   // c.f. https://docs.python.org/3.3/c-api/arg.html#PyArg_ParseTuple
//if (!PyArg_ParseTuple(args, "O", &x_obj)) return NULL;
//lNbMax = PyLong_AsLong(x_obj); // c.f. https://docs.python.org/3.3/c-api/long.html

vRep = 0.0;
for (lNbr=lNbMax-1; lNbr>=lNbMin; lNbr--) vRep = vRep + 1.0 / lNbr;

return PyFloat_FromDouble(vRep);
// https://docs.python.org/3.3/c-api/float.html
} // test3_sommeInv

// Définit l'aide associée à la fonction.
#define AIDE "sommeInv prend deux nombres entiers Nmin et Nmax en entrée\n\
et retourne la somme des inverses des entiers de 1/Nmin à 1/(Nmax-1)."

static PyMethodDef test3Methods[] = {
//===================================
{"sommeInv",  test3_sommeInv, METH_VARARGS,
  AIDE}, // Est un texte d'aide. Obtenu avec : print(test3.sommeInv.__doc__)
 {NULL, NULL, 0, NULL}  // Sentinel 
};

static struct PyModuleDef test3module = {
//=======================================
// Est une structure nécessaire pour Python ???
  PyModuleDef_HEAD_INIT,
  "test3",   /* name of module */
  NULL,  //test3_doc, /* module documentation, may be NULL */
  -1,       /* size of per-interpreter state of the module,
             or -1 if the module keeps state in global variables. */
  test3Methods
};

PyMODINIT_FUNC PyInit_test3(void) {
//=================================
// Fonction de création du module
return PyModule_Create(&test3module);
} // PyInit_test3

int main(int argc, char *argv[]) {
//================================
// Fonction principale, qui sera exécutée au chargement du module
wchar_t *program = Py_DecodeLocale(argv[0], NULL);
if (program == NULL) {
  fprintf(stderr, "Fatal error: cannot decode argv[0]\n");
  exit(1);
  }

/* Add a built-in module, before Py_Initialize */
PyImport_AppendInittab("test3", PyInit_test3);

/* Pass argv[0] to the Python interpreter */
Py_SetProgramName(program);

/* Initialize the Python interpreter.  Required. */
Py_Initialize();

/* Optionally import the module; alternatively,
   import can be deferred until the embedded script
   imports it. */
PyImport_ImportModule("test3");

PyMem_RawFree(program);
return 0;
} // main


test3.py   un script Python qui appelle la fonction écrite en C.
"""
test3.py
Ceci est un exemple d'appel à une routine écrite en langage C.

Test la librairie de démo "test3.c"

"""

import test3
print(test3.sommeInv.__doc__) # pour afficher de l'aide sur la fonction.
print()

print(test3.sommeInv(10, 100)) # 1/10 + 1/11 + 1/12 + 1/13 + 1/14 + ... + 1/(N-1)
print(test3.sommeInv(100, 1000)) # 1/100 + 1/101 + 1/102 + 1/103 + ... + 1/(N-1)
print(test3.sommeInv(1000, 10000)) # 1/1000 + 1/1001 + 1/1002 + ... + 1/(N-1)
print(test3.sommeInv(10000, 100000)) # 1/10000 + 1/10001 + 1/10002 + ... + 1/(N-1)
print()

nn=10
while (nn < 100000000):
    print(test3.sommeInv(nn, 10*nn))
    nn=10*nn

# On remarque que ces sommes donnent presque le même résultat.
# Ceci suggère très fortement que la série harmonique diverge
# et montre également qu'elle diverge comme Ln(N).
# La constante d'Euler-Mascheroni est définie par
# gamme = lim(N -> infini) (somme 1/k - Ln(N)) = 0,577 215 664 901 532 860 6…
# c.f. https://fr.wikipedia.org/wiki/Constante_d%27Euler-Mascheroni

print()
import math
nn=100000000
print("gama = ", test3.sommeInv(1, nn) - math.log(nn))

test4.c   TOP
/*
test4.c
L'objectif est d'avoir une soubroutine en C
qui peut être accédé depuis le langage Python.

Pour compiler depuis un Terminal :
gcc -shared -o test4.so -fPIC test4.c

Depuis Python, exécuter le script test4.py qui est donné dans un autre fichier.

Pour les types que Python reconnait et peut convertir en C :
https://www.tutorialspoint.com/python/python_further_extensions.htm
***********************************************************************/

#include "Python.h"

static PyObject *test4_sommeInv9(PyObject *self, PyObject *args) {
//===============================================================
// Définit la fonction en C qui sera appelée depuis Python
// PyObject *x_obj;  // autre possibilité.
// Somme des inverses des nombres entiers de lNbMin à lNbMax-1
// lNbMin et lNbMax sont les deux arguments.
// test4_sommeInv9(1,10) = 2.8178571428571426;  // La somme de 1 à 8
// test4_sommeInv9(10,100) = 2.063991622224917;  // La somme de 10 à 88
// test4_sommeInv9(100,1000) = 1.818871425200977;  // La somme de 1 à 8888
// test4_sommeInv9(1000,10000) = 1.633464212583175;  // La somme de 1 à 88888
long lNbMax;
long lNbMin;
long lNbr;
long lDixPow;
double vRep;

if (!PyArg_ParseTuple(args, "ll", &lNbMin, &lNbMax)) return NULL; 
//if (!PyArg_ParseTuple(args, "O", &x_obj)) return NULL;
//lNbMax = PyLong_AsLong(x_obj); // c.f. https://docs.python.org/3.3/c-api/long.html

vRep = 0.0;
lNbr=lNbMin;
while (lNbr<lNbMax) {
  if (lNbr % 10 == 9) { lNbr ++;
    if (lNbr/10 % 10 == 9) { lNbr += 10;
      if (lNbr/100 % 10 == 9) { lNbr += 100;
        // J'ai laissé quelques "if" pour comprendre la structures des tests.
        lDixPow = 1000;
        while (lNbr/lDixPow % 10 == 9) {
          lNbr += lDixPow;
          lDixPow *=10;
          }
        }      
      }
    }
  vRep = vRep + 1.0 / lNbr;
  lNbr++;
  } // while

return PyFloat_FromDouble(vRep);
// https://docs.python.org/3.3/c-api/float.html
} // test4_sommeInv9

// Définit l'aide associée à la fonction.
#define AIDE "sommeInv9 prend deux nombres entiers Nmin et Nmax en entrée\n\
et retourne la somme des inverses des entiers de 1/Nmin à 1/(Nmax-1)\n\
dans laquelle tous les nombres contenant le chiffre 9\n\
ont été éliminés. Voici des nombres éliminés :\n\
9 ; 19 ; 29 ; 9xxx ; 19xxx ; 29xxx ; 999999.\n\
Nmin ne doit pas contenir de chiffre 9."

static PyMethodDef test4Methods[] = {
//===================================
{"sommeInv9",  test4_sommeInv9, METH_VARARGS,
  AIDE}, // Est un texte d'aide. Obtenu avec : print(test4.sommeInv9.__doc__)
 {NULL, NULL, 0, NULL}  // Sentinel 
};

static struct PyModuleDef test4module = {
//=======================================
// Est une structure nécessaire pour Python ???
  PyModuleDef_HEAD_INIT,
  "test4",   /* name of module */
  NULL,  //test4_doc, /* module documentation, may be NULL */
  -1,       /* size of per-interpreter state of the module,
             or -1 if the module keeps state in global variables. */
  test4Methods
};

PyMODINIT_FUNC PyInit_test4(void) {
//=================================
// Fonction de création du module
return PyModule_Create(&test4module);
} // PyInit_test4

int main(int argc, char *argv[]) {
//================================
// Fonction principale, qui sera exécutée au chargement du module
wchar_t *program = Py_DecodeLocale(argv[0], NULL);
if (program == NULL) {
  fprintf(stderr, "Fatal error: cannot decode argv[0]\n");
  exit(1);
  }

/* Add a built-in module, before Py_Initialize */
PyImport_AppendInittab("test4", PyInit_test4);

/* Pass argv[0] to the Python interpreter */
Py_SetProgramName(program);

/* Initialize the Python interpreter.  Required. */
Py_Initialize();

/* Optionally import the module; alternatively,
   import can be deferred until the embedded script
   imports it. */
PyImport_ImportModule("test4");

PyMem_RawFree(program);
return 0;
} // main

test4.py   un script Python qui appelle la fonction écrite en C.
"""
test4.py
Ceci est un exemple d'appel à une routine écrite en langage C.

Test la librairie de démo "test4.c"

"""

import test4
print(test4.sommeInv9.__doc__) # pour afficher de l'aide sur la fonction.
print()

print(test4.sommeInv9(1, 10)) # 1/1 + 1/2 + 1/3 + 1/4 + ... + 1/8 (1/9 est éliminé)
print(test4.sommeInv9(10, 100)) # 1/10 + 1/11 + 1/12 + 1/13 + 1/14 + ... + 1/88
   # Eliminé : 1/19, 1/29, ..., 1/79, 1/89 à 1/99
print(test4.sommeInv9(100, 1000)) # 1/100 + 1/101 + 1/102 + 1/103 + ... + 1/888
print(test4.sommeInv9(1000, 10000)) # 1/1000 + 1/1001 + 1/1002 + ... + 1/8888
print(test4.sommeInv9(10000, 100000)) # 1/10000 + 1/10001 + 1/10002 + ... + 1/88888
print()

nn=10
s2 = test4.sommeInv9(nn, 10*nn)
while (nn < 100000000):
    s1 = s2
    nn=10*nn
    s2 = test4.sommeInv9(nn, 10*nn)
    print("Rapport =", s2/s1)

# On remarque que le rapport de ces sommes tend vers 0,9
# Ceci suggère très fortement que cette série converge
# comme 0.9^Log10(N)
# Donc la convergence est très lente.

nn = 10000000
s1 = test4.sommeInv9(1, nn)
s2 = test4.sommeInv9(nn, 10*nn)
# La valeur limite est environ :
# s1 + s2*(1 + 0.9 + 0.9^2 + 0.9^3 + 0.9^4 + ...) = s1 + 10*s2
print("Valeur limite =", s1 + 10*s2)
# On montre par d'autres méthodes que la valeur limite vaut bien :
# 22,92067 66192 64150 34816 ...

test5.c   TOP
L'exemple qui suit, implémente deux fonctions dans le fichiers .c
C'est la structure : "static PyMethodDef test5Methods[] = {" qui contient un élément de plus.
Le fichier : test5.c
Le fichier : test5.py

test6.c   TOP
Évolution du test5.
Le test7, est beaucoup plus complet, du point de vue mathématique, avec quelques exemples d'implémentation informatique.
Le fichier : test6.c
Le fichier : test6.py

test7.c   TOP
Grosse évolution mathématique de problèmes qui apparaîssent dans les tests 5 et 6.
Résolution d'un problème mathématique de convergence extrêmement lent d'une série, donc j'ai écrit un développement dans un fichier .pdf ( version .odt ).
Références : "Summing the curious series of Kempner and Irwin", de Robert Baillie "arXiv:0806.4410v4 [math.CA] 27 Aug 2015
et un .pdf de l'article
Le fichier : test7.c
Le fichier : test7.py

test8.c   TOP
Un simple exemple d'entrée d'un "string" (chaîne de caractères), qui retourne la même chaîne, suivi du code utf-8 de chaque caractère.
Il y a une subtilité qu'en UTF-8, les caractères accentués sont codés sur deux octets.
Certain caractère sont codés sur trois, voir 4 octets.
Il y a aussi un exemple d'envoie d'une liste Python, de longueur arbitraire. La liste inversée, suivit de sa somme est retourné sous forme de liste Python.
Une liste en Python correspond à un "Array" (un tableau" dans d'autres langages.
Le fichier : test8.c
Le fichier : test8.py


Raspberry Pi - How to start programming with Python
Plusieurs liens intéressants également.
Raspberry Pi Programming For Beginners - 12 Basic Tutorials - Program a Simple Game
Raspberry Pi Robotics #1: GPIO Control
Raspberry Pi: 8 Channel Relay step-by-step with software examples for automation


Plan du Site : Home   arrow   Python   arrow   python_code_c.html ( = http://www.juggling.ch/gisin/python/python_code_c.html )


Page mise à jour le 15 mars 2018 par Bernard Gisin     ( Envoyer un e-mail )
Hébergement par : www.infomaniak.ch