我无法找到ctypes如何弥合std::vector
和Python之间的差距;没有在互联网上提到的组合。这是不好的做法,它不存在还是我错过了什么?
C ++ :xxx.cpp
#include <fstream>
#include <string>
using namespace std;
extern "C" std::vector<int> foo(const char* FILE_NAME)
{
string line;
std::vector<int> result;
ifstream myfile(FILE_NAME);
while (getline(myfile, line)) {
result.push_back(1);
}
return(result);
}
Python: xxx.py
import ctypes
xxx = ctypes.CDLL("./libxxx.so")
xxx.foo.argtypes = ??????
xxx.foo.restype = ??????
答案 0 :(得分:14)
这种方法是否真的提供了更快的执行时间,我将解释一下你将如何做到这一点。基本上,创建一个指向C ++ vector
的指针,它可以通过C函数与Python连接。然后,您可以将C ++代码包装在Python类中,隐藏ctypes
。
我包含了我认为有用的魔术方法包含在Python类中。您可以选择删除它们或添加更多以满足您的需求。析构函数非常重要。
// vector_python.cpp
#include <vector>
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
extern "C" void foo(vector<int>* v, const char* FILE_NAME){
string line;
ifstream myfile(FILE_NAME);
while (getline(myfile, line)) v->push_back(1);
}
extern "C" {
vector<int>* new_vector(){
return new vector<int>;
}
void delete_vector(vector<int>* v){
cout << "destructor called in C++ for " << v << endl;
delete v;
}
int vector_size(vector<int>* v){
return v->size();
}
int vector_get(vector<int>* v, int i){
return v->at(i);
}
void vector_push_back(vector<int>* v, int i){
v->push_back(i);
}
}
将其编译为共享库。在Mac OS X上,这可能看起来像,
g++ -c -fPIC vector_python.cpp -o vector_python.o
g++ -shared -Wl,-install_name,vector_python_lib.so -o vector_python_lib.so vector_python.o
from ctypes import *
class Vector(object):
lib = cdll.LoadLibrary('vector_python_lib.so') # class level loading lib
lib.new_vector.restype = c_void_p
lib.new_vector.argtypes = []
lib.delete_vector.restype = None
lib.delete_vector.argtypes = [c_void_p]
lib.vector_size.restype = c_int
lib.vector_size.argtypes = [c_void_p]
lib.vector_get.restype = c_int
lib.vector_get.argtypes = [c_void_p, c_int]
lib.vector_push_back.restype = None
lib.vector_push_back.argtypes = [c_void_p, c_int]
lib.foo.restype = None
lib.foo.argtypes = [c_void_p]
def __init__(self):
self.vector = Vector.lib.new_vector() # pointer to new vector
def __del__(self): # when reference count hits 0 in Python,
Vector.lib.delete_vector(self.vector) # call C++ vector destructor
def __len__(self):
return Vector.lib.vector_size(self.vector)
def __getitem__(self, i): # access elements in vector at index
if 0 <= i < len(self):
return Vector.lib.vector_get(self.vector, c_int(i))
raise IndexError('Vector index out of range')
def __repr__(self):
return '[{}]'.format(', '.join(str(self[i]) for i in range(len(self))))
def push(self, i): # push calls vector's push_back
Vector.lib.vector_push_back(self.vector, c_int(i))
def foo(self, filename): # foo in Python calls foo in C++
Vector.lib.foo(self.vector, c_char_p(filename))
然后你可以在解释器中测试它(file.txt
只包含三行乱码)。
>>> from vector import Vector
>>> a = Vector()
>>> a.push(22)
>>> a.push(88)
>>> a
[22, 88]
>>> a[1]
88
>>> a[2]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "vector.py", line 30, in __getitem__
raise IndexError('Vector index out of range')
IndexError: Vector index out of range
>>> a.foo('file.txt')
>>> a
[22, 88, 1, 1, 1]
>>> b = Vector()
>>> ^D
destructor called in C++ for 0x1003884d0
destructor called in C++ for 0x10039df10
答案 1 :(得分:6)
特别的原因是速度很重要。我正在创造一个 应该能够处理大数据的应用程序。在200,000行 缺失必须计入300个值(200k乘300矩阵)。一世 相信,但如果我错了,请纠正我,C ++将是显着的 更快。
好吧,如果您正在从一个大文件中读取,那么您的进程将主要受IO限制,因此Python和C之间的时间可能不会有显着差异。
以下代码......
result = []
for line in open('test.txt'):
result.append(line.count('NA'))
...似乎和我在C中可以一起攻击的任何东西一样快,尽管它使用的是optimized algorithm我并不是很熟悉。
处理200,000行只需不到一秒钟,虽然我有兴趣看看你是否可以写一个明显更快的C函数。
<强>更新强>
如果你想在C中完成它,并最终得到一个Python列表,那么使用Python/C API自己构建列表可能更有效,而不是构建一个std::vector
然后转换为稍后是Python列表。
一个示例,它只返回0到99之间的整数列表...
// hack.c
#include <python2.7/Python.h>
PyObject* foo(const char* filename)
{
PyObject* result = PyList_New(0);
int i;
for (i = 0; i < 100; ++i)
{
PyList_Append(result, PyInt_FromLong(i));
}
return result;
}
编译......
$ gcc -c hack.c -fPIC
$ ld -o hack.so -shared hack.o -lpython2.7
使用示例......
>>> from ctypes import *
>>> dll = CDLL('./hack.so')
>>> dll.foo.restype = py_object
>>> dll.foo('foo')
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ...]
答案 2 :(得分:3)
基本上,从动态加载的库中返回C ++对象并不是一个好主意。要在Python代码中使用C ++ vector
,您必须教Python处理C ++对象(这包括可以使用新版本的C ++编译器或STL更改的对象的二进制表示)。
ctypes
允许您使用C类型与库进行交互。不是C ++。
也许问题可以通过boost::python
解决,但使用普通C进行交互看起来更可靠。