Question

Je réfléchis depuis un moment à l'écriture d'une bibliothèque d'ajustement de pointe.Je connais assez bien Python et je prévois de tout implémenter en Python pour commencer, mais j'envisage de devoir éventuellement réimplémenter certaines routines de base dans un langage compilé.

IIRC, l'une des missions originales de Python était de servir de langage de prototypage, mais Python est assez libéral en permettant de transmettre des fonctions, des foncteurs et des objets à des fonctions et des méthodes, alors que je soupçonne qu'il n'en va pas de même pour C ou Fortran, par exemple.

Que dois-je savoir sur la conception de fonctions/classes qui, selon moi, devront s'interfacer avec le langage compilé ?Et dans quelle mesure ces problèmes potentiels sont traités par des bibliothèques telles que cTypes, bgen, LAMPÉE, Boost.Python, Cython ou Python SIP?

Pour ce cas d'utilisation particulier (une bibliothèque d'ajustement), j'imagine permettre aux utilisateurs de définir des fonctions mathématiques (guassiennes, lorentziennes, etc.) en tant que fonctions Python qui peuvent ensuite être transmises et interprétées par la bibliothèque d'ajustement de code compilée.Passer et renvoyer des tableaux est également essentiel.

Était-ce utile?

La solution

Enfin une question à laquelle je peux vraiment apporter une réponse intéressante :).

J'ai étudié f2py, boost.python, swig, cython et pyrex pour mon travail (doctorat en techniques de mesure optique).J'ai beaucoup utilisé swig, boost.python un peu et beaucoup pyrex et cython.J'ai également utilisé des ctypes.Voici ma répartition :

Clause de non-responsabilité:C'est mon expérience personnelle.Je ne suis impliqué dans aucun de ces projets.

lampée:ne fonctionne pas bien avec C++.Cela devrait être le cas, mais les problèmes de modification des noms lors de l'étape de liaison ont été un casse-tête majeur pour moi sous Linux et Mac OS X.Si vous avez du code C et que vous souhaitez l'interfacer avec python, c'est une bonne solution.J'ai enveloppé le GTS pour mes besoins et j'avais besoin d'écrire essentiellement une bibliothèque partagée en C à laquelle je pourrais me connecter.Je ne le recommanderais pas.

Types :J'ai écrit un wrapper libdc1394 (bibliothèque de caméra IEEE) en utilisant des ctypes et ce fut une expérience très simple.Vous pouvez trouver le code sur https://launchpad.net/pydc1394.La conversion des en-têtes en code python demande beaucoup de travail, mais tout fonctionne de manière fiable.C'est un bon moyen si vous souhaitez interfacer une bibliothèque externe.Ctypes est également dans la stdlib de python, afin que tout le monde puisse utiliser votre code immédiatement.C'est aussi un bon moyen de jouer rapidement avec une nouvelle bibliothèque en python.Je peux le recommander pour s'interfacer avec des bibliothèques externes.

Boost.Python:Très agréable.Si vous disposez déjà de votre propre code C++ que vous souhaitez utiliser en python, optez pour celui-ci.Il est très facile de traduire les structures de classes C++ en structures de classes Python de cette façon.Je le recommande si vous avez du code C++ dont vous avez besoin en python.

Pyrex/Cython : Utilisez Cython, pas Pyrex.Période.Cython est plus avancé et plus agréable à utiliser.Aujourd'hui, je fais avec Cython tout ce que je faisais avec SWIG ou Ctypes.C'est également le meilleur moyen si votre code Python s'exécute trop lentement.Le processus est absolument fantastique :vous convertissez vos modules python en modules cython, les construisez et continuez à profiler et à optimiser comme s'il s'agissait toujours de python (aucun changement d'outils nécessaire).Vous pouvez ensuite appliquer autant (ou aussi peu) de code C mélangé à votre code python.C'est de loin plus rapide que de devoir réécrire des parties entières de votre application en C ;vous réécrivez uniquement la boucle interne.

Horaires:ctypes a la surcharge d'appel la plus élevée (~ 700 ns), suivi de boost.python (322 ns), puis directement de swig (290 ns).Cython a la surcharge d'appel la plus faible (124 ns) et le meilleur retour sur lequel il passe du temps (prise en charge de cProfile !).Les nombres proviennent de ma boîte appelant une fonction triviale qui renvoie un entier à partir d'un shell interactif ;La surcharge d’importation de module n’est donc pas chronométrée, seule la surcharge d’appel de fonction l’est.Il est donc plus simple et plus productif d'obtenir rapidement du code Python en profilant et en utilisant Cython.

Résumé:Pour votre problème, utilisez Cython ;).J'espère que ce récapitulatif sera utile à certaines personnes.Je répondrai volontiers à toute question restante.


Modifier:J'oublie de mentionner :à des fins numériques (c'est-à-dire pour la connexion à NumPy), utilisez Cython ;ils ont un soutien pour cela (car ils développent essentiellement du cython à cet effet).Cela devrait donc être un autre +1 pour votre décision.

Autres conseils

Je n'ai pas utilisé SWIG ou SIP, mais je trouve qu'écrire des wrappers Python avec boost.python être très puissant et relativement facile à utiliser.

Je ne sais pas exactement quelles sont vos exigences en matière de transmission de types entre C/C++ et python, mais vous pouvez le faire facilement soit en exposant un type C++ à python, soit en utilisant un type générique. boost :: python :: objet argument à votre API C++.Vous pouvez également enregistrer des convertisseurs pour convertir automatiquement les types Python en types C++ et vice versa.

Si vous prévoyez d'utiliser boost.python, le Didacticiel est un bon point de départ.

J'ai implémenté quelque chose de similaire à ce dont vous avez besoin.J'ai une fonction C ++ qui accepte une fonction Python et une image comme arguments, et applique la fonction Python à chaque pixel de l'image.

Image* unary(boost::python::object op, Image& im)
{
    Image* out = new Image(im.width(), im.height(), im.channels());
    for(unsigned int i=0; i<im.size(); i++)
    {
        (*out)[i] == extract<float>(op(im[i]));
    }
    return out;
}

Dans ce cas, Image est un objet C++ exposé à Python (une image avec des pixels flottants) et op est une fonction définie par Python (ou en fait n'importe quel objet Python avec un attribut __call__).Vous pouvez ensuite utiliser cette fonction comme suit (en supposant qu'unaire se trouve dans l'image appelée qui contient également Image et une fonction de chargement) :

import image
im = image.load('somefile.tiff')
double_im = image.unary(lambda x: 2.0*x, im)

Quant à l'utilisation de tableaux avec boost, je ne l'ai personnellement pas fait, mais je sais que la fonctionnalité permettant d'exposer des tableaux à Python à l'aide de boost est disponible - ce pourrait être utile.

La meilleure façon de planifier une éventuelle transition vers le code compilé est d'écrire les parties sensibles aux performances sous la forme d'un module de fonctions simples dans un style fonctionnel (apatrides et sans effets secondaires), qui acceptent et renvoient des types de données de base.

Cela fournira un mappage un à un de votre code prototype Python au code compilé éventuel, et vous permettra d'utiliser types facilement et évitez tout un tas de maux de tête.

Pour un ajustement optimal, vous devrez certainement utiliser des tableaux, ce qui compliquera un peu les choses, mais cela reste tout à fait faisable avec les ctypes.

Si vous souhaitez vraiment utiliser des structures de données plus compliquées, ou modifier les arguments passés, LAMPÉE ou Interface d'extension C standard de Python vous laissera faire ce que vous voulez, mais avec quelques tracas.

Pour ce que vous faites, vous voudrez peut-être aussi consulter NumPy, qui pourrait effectuer une partie du travail que vous voudriez pousser vers C, tout en offrant une aide supplémentaire pour déplacer les données entre Python et C.

f2py (partie de numpy) est une alternative plus simple à SWIG et boost.python pour encapsuler le code de traitement des nombres C/Fortran.

D'après mon expérience, il existe deux façons simples d'appeler du code C à partir du code Python.Il existe d’autres approches, toutes plus ennuyeuses et/ou verbeuses.

La première et la plus simple consiste à compiler un tas de code C en tant que bibliothèque partagée distincte, puis à appeler les fonctions de cette bibliothèque à l'aide de ctypes.Malheureusement, transmettre autre chose que des types de données de base n'est pas trivial.

La deuxième méthode la plus simple consiste à écrire un module Python en C, puis à appeler des fonctions dans ce module.Vous pouvez transmettre tout ce que vous voulez à ces fonctions C sans avoir à franchir des obstacles.Et il est facile d'appeler des fonctions ou des méthodes Python à partir de ces fonctions C, comme décrit ici : https://docs.python.org/extending/extending.html#calling-python-functions-from-c

Je n'ai pas assez d'expérience avec SWIG pour proposer des commentaires intelligents.Et bien qu'il soit possible de faire des choses comme transmettre des objets Python personnalisés aux fonctions C via des ctypes, ou définir de nouvelles classes Python en C, ces choses sont ennuyeuses et verbeuses et je recommande d'adopter l'une des deux approches décrites ci-dessus.

Python est assez libéral en permettant de transmettre des fonctions, des foncteurs et des objets à des fonctions et des méthodes, alors que je soupçonne qu'il n'en va pas de même pour C ou Fortran, par exemple.

En C, vous ne pouvez pas passer une fonction comme argument à une fonction, mais vous pouvez passer un pointeur de fonction qui est tout aussi efficace.

Je ne sais pas à quel point cela vous aiderait lorsque vous essayez d'intégrer du code C et Python, mais je voulais juste dissiper une idée fausse.

En plus des outils ci-dessus, je peux recommander d'utiliser Pyrex (pour créer des modules d'extension Python) ou Psycho (en tant que compilateur JIT pour Python).

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top