Wywołujesz C / c++ z Pythona? [zamknięte]
chcesz poprawić to pytanie? Update the question so it edytując ten post.
Zamknięte 4 miesiące temu .
Popraw to pytanieJaki byłby najszybszy sposób na zbudowanie wiązania Pythona do biblioteki C lub c++?
(używam Windows, jeśli to ma znaczenie.)
12 answers
Powinieneś rzucić okiem na Boost.Python . Oto krótkie wprowadzenie zaczerpnięte z ich strony internetowej:
Biblioteka Pythona Boost jest frameworkiem do interfejsów Pythona i C++. Pozwala szybko i bezproblemowo wystawiać klasy C++ funkcji i obiektów do Pythona i odwrotnie, nie używając specjalnych narzędzia - tylko kompilator C++. Przeznaczony jest do zawijania interfejsów C++ nieinwazyjne, tak aby nie trzeba było zmieniać kodu C++ w wszystko po to, aby zawiń go, robi Boost.Python idealny do ekspozycji Biblioteki innych firm do Pythona. Korzystanie z biblioteki z zaawansowanych techniki metaprogramowania upraszczają jego składnię dla użytkowników, dzięki czemu wrapping code przybiera wygląd pewnego rodzaju deklaratywnego interfejsu język definicji (IDL).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-04 22:58:57
Moduł Ctypes jest częścią biblioteki standardowej i dlatego jest bardziej stabilny i szeroko dostępny niż swig , który zawsze sprawiał mi Problemy.
Z ctypes, musisz zaspokoić wszelkie zależności w czasie kompilacji od Pythona, a twoje Wiązanie będzie działać na każdym Pythonie, który ma ctypes, a nie tylko ten, z którym został skompilowany.
Załóżmy, że masz prostą klasę przykładową C++, z którą chcesz rozmawiać w pliku o nazwie foo.cpp:
#include <iostream>
class Foo{
public:
void bar(){
std::cout << "Hello" << std::endl;
}
};
Od ctypes może rozmawiać tylko z funkcjami C, musisz podać te deklarujące je jako extern"C"
extern "C" {
Foo* Foo_new(){ return new Foo(); }
void Foo_bar(Foo* foo){ foo->bar(); }
}
Następnie musisz skompilować to do biblioteki współdzielonej
g++ -c -fPIC foo.cpp -o foo.o
g++ -shared -Wl,-soname,libfoo.so -o libfoo.so foo.o
I na koniec trzeba napisać wrapper Pythona (np. w fooWrapper.py)
from ctypes import cdll
lib = cdll.LoadLibrary('./libfoo.so')
class Foo(object):
def __init__(self):
self.obj = lib.Foo_new()
def bar(self):
lib.Foo_bar(self.obj)
Kiedy już to masz, możesz to nazwać jak
f = Foo()
f.bar() #and you will see "Hello" on the screen
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-07 15:48:56
Najszybszym sposobem jest użycie SWIG .
Przykład ze SWIG tutorial :
/* File : example.c */
int fact(int n) {
if (n <= 1) return 1;
else return n*fact(n-1);
}
Plik interfejsu:
/* example.i */
%module example
%{
/* Put header files here or function declarations like below */
extern int fact(int n);
%}
extern int fact(int n);
Budowanie modułu Pythona na Uniksie:
swig -python example.i
gcc -fPIC -c example.c example_wrap.c -I/usr/local/include/python2.7
gcc -shared example.o example_wrap.o -o _example.so
Użycie:
>>> import example
>>> example.fact(5)
120
Zauważ, że musisz mieć python-dev. Również w niektórych systemach pliki nagłówkowe Pythona będą znajdować się w /usr/include/python2.7 w zależności od sposobu instalacji.
Z tutoriala:
SWIG jest dość kompletnym kompilatorem C++ z obsługą prawie każda funkcja językowa. Obejmuje to wstępne przetwarzanie, wskaźniki, klasy, dziedziczenie, a nawet szablony C++. SWIG może być również używany do pakowania struktur i klas w klasy proxy w języku docelowym - ujawniając podstawową funkcjonalność w bardzo naturalny sposób.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-14 17:39:50
Rozpocząłem swoją podróż w wiązaniu Pythona C++ od tej strony, w celu połączenia typów danych wysokiego poziomu (wielowymiarowych wektorów STL z listami Pythona): -)
Po wypróbowaniu rozwiązań opartych zarówno na ctyp jak i boost.python (i nie będąc inżynierem oprogramowania) uznałem je za złożone, gdy wymagane jest Wiązanie typów danych wysokiego poziomu, podczas gdy znalazłem SWIG znacznie prostsze w takich przypadkach.
Ten przykład wykorzystuje zatem SWIG, i został przetestowany w Linuksie (ale SWIG jest dostępny i jest szeroko stosowany również w Windows).
Celem jest udostępnienie Pythonowi funkcji C++, która przyjmuje macierz w postaci wektora 2D STL i Zwraca średnią z każdego wiersza (jako wektor 1D STL).
Kod w C++ ("kod.cpp") jest następująca:
#include <vector>
#include "code.h"
using namespace std;
vector<double> average (vector< vector<double> > i_matrix) {
// Compute average of each row..
vector <double> averages;
for (int r = 0; r < i_matrix.size(); r++){
double rsum = 0.0;
double ncols= i_matrix[r].size();
for (int c = 0; c< i_matrix[r].size(); c++){
rsum += i_matrix[r][c];
}
averages.push_back(rsum/ncols);
}
return averages;
}
Równoważny nagłówek ("code.h") to:
#ifndef _code
#define _code
#include <vector>
std::vector<double> average (std::vector< std::vector<double> > i_matrix);
#endif
Najpierw kompilujemy kod C++, aby utworzyć plik obiektowy:
g++ -c -fPIC code.cpp
Następnie definiujemy plik definicji interfejsu SWIG ("Kod.i") dla naszych funkcji C++.
%module code
%{
#include "code.h"
%}
%include "std_vector.i"
namespace std {
/* On a side note, the names VecDouble and VecVecdouble can be changed, but the order of first the inner vector matters! */
%template(VecDouble) vector<double>;
%template(VecVecdouble) vector< vector<double> >;
}
%include "code.h"
Używając SWIG, generujemy kod źródłowy interfejsu C++ z pliku definicji interfejsu SWIG..
swig -c++ -python code.i
W końcu kompilujemy wygenerowany plik źródłowy interfejsu C++ i łączymy wszystko razem, aby wygenerować współdzieloną bibliotekę, która jest bezpośrednio importowana przez Pythona ("_"ma znaczenie):
g++ -c -fPIC code_wrap.cxx -I/usr/include/python2.7 -I/usr/lib/python2.7
g++ -shared -Wl,-soname,_code.so -o _code.so code.o code_wrap.o
Możemy teraz używać funkcji w skryptach Pythona:
#!/usr/bin/env python
import code
a= [[3,5,7],[8,10,12]]
print a
b = code.average(a)
print "Assignment done"
print a
print b
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-04 23:05:25
Istnieje również pybind11
, który jest jak lekka wersja Boost.Python i kompatybilny ze wszystkimi nowoczesnymi kompilatorami C++:
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-18 15:43:41
Dla nowoczesnego C++, użyj cppyy: http://cppyy.readthedocs.io/en/latest/
Jest oparty na Cling, interpreterze C++ Dla Clang / LLVM. Wiązania są w czasie wykonywania i nie jest wymagany dodatkowy język pośredni. Dzięki Clang obsługuje C++17.
Zainstaluj go za pomocą pip:
$ pip install cppyy
W przypadku małych projektów wystarczy załadować odpowiednią bibliotekę i nagłówki, które Cię interesują. Np. weź kod z ctypes przykład jest ten wątek, ale podziel w nagłówku i sekcje kodu:
$ cat foo.h
class Foo {
public:
void bar();
};
$ cat foo.cpp
#include "foo.h"
#include <iostream>
void Foo::bar() { std::cout << "Hello" << std::endl; }
Skompilować:
$ g++ -c -fPIC foo.cpp -o foo.o
$ g++ -shared -Wl,-soname,libfoo.so -o libfoo.so foo.o
I użyj go:
$ python
>>> import cppyy
>>> cppyy.include("foo.h")
>>> cppyy.load_library("foo")
>>> from cppyy.gbl import Foo
>>> f = Foo()
>>> f.bar()
Hello
>>>
Duże projekty są obsługiwane przez automatyczne ładowanie przygotowanych informacji o odbiciach i fragmentów cmake do ich tworzenia, dzięki czemu użytkownicy zainstalowanych pakietów mogą po prostu uruchomić:
$ python
>>> import cppyy
>>> f = cppyy.gbl.Foo()
>>> f.bar()
Hello
>>>
Dzięki LLVM możliwe są zaawansowane funkcje, takie jak automatyczna instancja szablonu. Aby kontynuować przykład:
>>> v = cppyy.gbl.std.vector[cppyy.gbl.Foo]()
>>> v.push_back(f)
>>> len(v)
1
>>> v[0].bar()
Hello
>>>
Uwaga: jestem autorem cppyy.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-06 18:21:21
Myślę, że cffi dla Pythona może być opcją.
Celem jest wywołanie kodu C z Pythona. Powinieneś być w stanie to zrobić bez nauki trzeciego języka: każda alternatywa wymaga od Ciebie naucz się własnego języka (Cython, SWIG) lub API (ctypes). Więc staraliśmy się założyć, że znasz Pythona i C i zminimalizować dodatkowe bity API, którego musisz się nauczyć.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-05 10:39:23
Pytanie brzmi, jak wywołać funkcję C z Pythona, jeśli dobrze zrozumiałem. Następnie najlepiej są Ctypy (BTW przenośne we wszystkich wariantach Pythona).
>>> from ctypes import *
>>> libc = cdll.msvcrt
>>> print libc.time(None)
1438069008
>>> printf = libc.printf
>>> printf("Hello, %s\n", "World!")
Hello, World!
14
>>> printf("%d bottles of beer\n", 42)
42 bottles of beer
19
Aby uzyskać szczegółowy przewodnik, zapoznaj się z moim artykułem na blogu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-28 08:08:19
Pybind11 minimal runnable przykład
Pybind11 był wcześniej wspominany w https://stackoverflow.com/a/38542539/895245 ale chciałbym podać tutaj konkretny przykład użycia i dalszą dyskusję na temat implementacji.
Ogólnie polecam pybind11, ponieważ jest naprawdę łatwy w użyciu: wystarczy dołączyć nagłówek, a następnie pybind11 używa magii szablonów, aby sprawdzić klasę C++, którą chcesz wystawić na działanie Pythona i robi to przejrzyście.
Minusem tego szablonu jest to, że spowalnia kompilację natychmiast dodając kilka sekund do dowolnego pliku, który używa pybind11, patrz na przykład dochodzenie przeprowadzone w tej sprawie . PyTorch zgadza się. Wniosek dotyczący rozwiązania tego problemu został złożony pod adresem: https://github.com/pybind/pybind11/pull/2445
Oto minimalny przykład, aby dać ci poczucie, jak niesamowite pybind11 jest:
Class_test.cpp
#include <string>
#include <pybind11/pybind11.h>
struct ClassTest {
ClassTest(const std::string &name) : name(name) { }
void setName(const std::string &name_) { name = name_; }
const std::string &getName() const { return name; }
std::string name;
};
namespace py = pybind11;
PYBIND11_PLUGIN(class_test) {
py::module m("my_module", "pybind11 example plugin");
py::class_<ClassTest>(m, "ClassTest")
.def(py::init<const std::string &>())
.def("setName", &ClassTest::setName)
.def("getName", &ClassTest::getName)
.def_readwrite("name", &ClassTest::name);
return m.ptr();
}
Class_test_main.py
#!/usr/bin/env python3
import class_test
my_class_test = class_test.ClassTest("abc");
print(my_class_test.getName())
my_class_test.setName("012")
print(my_class_test.getName())
assert(my_class_test.getName() == my_class_test.name)
Skompiluj i uruchom:
#!/usr/bin/env bash
set -eux
g++ `python3-config --cflags` -shared -std=c++11 -fPIC class_test.cpp \
-o class_test`python3-config --extension-suffix` `python3-config --libs`
./class_test_main.py
Ten przykład pokazuje, w jaki sposób pybind11 pozwala bez wysiłku wystawić klasę ClassTest
C++ na Pythona! Kompilacja tworzy plik o nazwie class_test.cpython-36m-x86_64-linux-gnu.so
, który class_test_main.py
automatycznie pobiera jako punkt definicji dla class_test
natywnie zdefiniowanego modułu.
Być może uświadomienie sobie, jak niesamowite jest to tylko zanurza się, jeśli próbujesz zrobić to samo ręcznie z natywnym API Pythona, zobacz na przykład ten przykład robienia tego, który ma około 10x więcej kodu: https://github.com/cirosantilli/python-cheat/blob/4f676f62e87810582ad53b2fb426b74eae52aad5/py_from_c/pure.c na tym przykładzie możesz zobaczyć, jak kod C musi boleśnie i jawnie definiować klasę Pythona bit po bit ze wszystkimi informacjami, które zawiera (członkowie, metody, dalsze metadane...). Zobacz też:
- czy rozszerzenie python-C++ może uzyskać obiekt C++ i wywołać jego członka funkcja?
- W przeciwieństwie do innych języków programowania, nie można używać języka C++.]}
- pełny i minimalny przykład Klasy (Nie metody) z rozszerzeniem Python C?
- osadzanie Pythona w C++ i wywoływanie metod z kodu C++ za pomocą Boost.Python
- dziedziczenie w Python C++ extension
Pybind11 twierdzi, że jest podobny do Pybind11 jest lekką biblioteką tylko nagłówkową, która eksponuje typy C++ w Pythonie i odwrotnie, głównie w celu tworzenia powiązań Pythona z istniejącym kodem C++. Jego cele i składnia są podobne do doskonałego Boost.Biblioteka Pythona autorstwa Davida Abrahamsa: minimalizacja kodu boilerplate w tradycyjnych modułach rozszerzeń poprzez wnioskowanie informacji o typie za pomocą introspekcja w czasie kompilacji. Główny problem z Boost.Python-a powodem stworzenia tak podobnego projektu-jest Boost. Boost jest ogromnie dużym i złożonym zestawem bibliotek narzędzi, które działają z prawie każdym istniejącym kompilatorem C++. Ta kompatybilność ma swój koszt: tajemne sztuczki szablonów i obejścia są niezbędne do obsługi najstarszych i najbardziej błędnych wzorów kompilatorów. Teraz, gdy Kompilatory kompatybilne z C++11 są powszechnie dostępne, ta ciężka maszyna stała się nadmiernie duża i niepotrzebna zależność. Pomyśl o tej bibliotece jako małej samodzielnej wersji Boost.Pythona ze wszystkim, co nie jest istotne dla generowania wiązań. Bez komentarzy, Podstawowe pliki nagłówkowe wymagają tylko ~4k linii kodu i zależą od Pythona (2.7 lub 3.x, lub PyPy2.7 >= 5.7) oraz biblioteki standardowej C++. Ta kompaktowa implementacja była możliwa dzięki nowym funkcjom języka C++11 (w szczególności: krotki, funkcje lambda i różne szablony). Od momentu powstania, Biblioteka ta rozrosła się poza Boost.Python na wiele sposobów, co prowadzi do znacznie prostszego wiązania kodu w wielu typowych sytuacjach. Pybind11 jest również jedyną nie natywną alternatywą, która jest dostępna w aktualnej dokumentacji wiążącej Microsoft Python C pod adresem: https://docs.microsoft.com/en-us/visualstudio/python/working-with-c-cpp-python-in-visual-studio?view=vs-2019 ( archiwum ). Testowane na Ubuntu 18.04, pybind11 2.0.1, Python 3.6.8, GCC 7.4.0.Boost.Python
, który został wymieniony na https://stackoverflow.com/a/145436/895245 jednak bardziej minimalistyczny, ponieważ jest uwolniony od nadęcia bycia wewnątrz projektu Boost:]}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-09-14 15:16:44
Cython jest zdecydowanie najlepszym rozwiązaniem, chyba że przewidujesz pisanie wrapperów Javy, w którym to przypadku preferowany może być SWIG.
Polecam korzystanie z narzędzia wiersza poleceń runcython
, sprawia, że proces korzystania z Cythona jest niezwykle łatwy. Jeśli chcesz przekazać dane strukturyzowane do C++, spójrz na bibliotekę protobuf Google, jest to bardzo wygodne.
Oto Minimalne przykłady, które zrobiłem, że używa obu narzędzi:
Https://github.com/nicodjimenez/python2cpp
Mam nadzieję, że może to być użyteczny punkt wyjścia.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-29 17:27:16
Najpierw powinieneś zdecydować, jaki jest twój konkretny cel. Oficjalna dokumentacja Pythona dotycząca rozszerzania i osadzania interpretera Pythona została wspomniana powyżej, Mogę dodać dobry Przegląd binarnych rozszerzeń. Przypadki użycia można podzielić na 3 kategorie:
- Moduły akceleratora : aby działać szybciej niż odpowiednik czystego kodu Pythona, uruchamia się w CPython.
- wrapper modules : Aby udostępnić istniejące interfejsy C Pythonowi kod.
- Low level system access: dostęp do funkcji niższego poziomu środowiska uruchomieniowego CPython, systemu operacyjnego lub podstawowego sprzętu.
Aby dać szerszą perspektywę innym zainteresowanym i ponieważ Twoje początkowe pytanie jest nieco niejasne ("do biblioteki C lub c++") myślę, że ta informacja może być dla Ciebie interesująca. Na powyższym linku możesz przeczytać o wadach używania rozszerzeń binarnych i ich alternatywach.
Oprócz innych sugerowane odpowiedzi, jeśli chcesz moduł akceleratora, możesz spróbować Numba . Działa on " poprzez generowanie zoptymalizowanego kodu maszynowego przy użyciu infrastruktury kompilatora LLVM w czasie importu, podczas pracy lub statycznie (przy użyciu dołączonego narzędzia pycc)".
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-05-15 11:17:09
Uwielbiam cppyy, dzięki temu bardzo łatwo rozszerzyć Pythona O kod C++, znacznie zwiększając wydajność w razie potrzeby.
Jest potężny i szczerze mówiąc bardzo prosty w użyciu,Oto przykład jak można utworzyć tablicę numpy i przekazać ją do funkcji klasy w C++.
Cppyy_test.py
import cppyy
import numpy as np
cppyy.include('Buffer.h')
s = cppyy.gbl.Buffer()
numpy_array = np.empty(32000, np.float64)
s.get_numpy_array(numpy_array.data, numpy_array.size)
print(numpy_array[:20])
Bufor.h
struct Buffer {
void get_numpy_array(double *ad, int size) {
for( long i=0; i < size; i++)
ad[i]=i;
}
};
Możesz również bardzo łatwo utworzyć moduł Pythona (za pomocą CMake), w ten sposób unikniesz przekompilowania C++ koduj cały czas.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-09 21:58:30