标签归档:ctypes

ctypes-初学者

问题:ctypes-初学者

我的任务是将ac库“包装”到python类中。在这个问题上,文档令人难以置信。看来他们希望只有高级python用户才能实现ctypes。好吧,我是python的初学者,需要帮助。

一些逐步的帮助将是很棒的。

所以我有我的C库。我该怎么办?我将哪些文件放在哪里?如何导入库?我读到可能有一种方法可以“自动包装”到Python?

(通过我在python.net上进行ctypes教程的方式,它不起作用。这意味着我认为他们认为我应该能够完成其余步骤。

实际上,这是我使用其代码得到的错误:

File "importtest.py", line 1
   >>> from ctypes import *
   SyntaxError: invalid syntax

我真的可以为此使用一些逐步的帮助!谢谢〜

I have the task of “wrapping” a c library into a python class. The docs are incredibly vague on this matter. It seems they expect only advanced python users would implement ctypes. Well i’m a beginner in python and need help.

Some step by step help would be wonderful.

So I have my c library. What do I do? What files do I put where? How do I import the library? I read that there might be a way to “auto wrap” to Python?

(By the way I did the ctypes tutorial on python.net and it doesn’t work. Meaning I’m thinking they are assuming I should be able to fill in the rest of the steps.

In fact this is the error I get with their code:

File "importtest.py", line 1
   >>> from ctypes import *
   SyntaxError: invalid syntax

I could really use some step by step help on this! Thanks~


回答 0

这是一个快速而肮脏的ctypes教程。

首先,编写您的C库。这是一个简单的Hello world示例:

测试库

#include <stdio.h>

void myprint(void);

void myprint()
{
    printf("hello world\n");
}

现在将其编译为共享库(可在此处找到macfix):

$ gcc -shared -Wl,-soname,testlib -o testlib.so -fPIC testlib.c

# or... for Mac OS X 
$ gcc -shared -Wl,-install_name,testlib.so -o testlib.so -fPIC testlib.c

然后,使用ctypes编写包装器:

testlibwrapper.py

import ctypes

testlib = ctypes.CDLL('/full/path/to/testlib.so')
testlib.myprint()

现在执行它:

$ python testlibwrapper.py

你应该看到输出

Hello world
$

如果您已经有了一个库,则可以跳过本教程的非python部分。确保ctypes可以通过将其放在/usr/lib另一个标准目录中来找到该库。如果这样做,则在编写包装程序时无需指定完整路径。如果选择不执行此操作,则在调用时必须提供库的完整路径ctypes.CDLL()

这里不是准备更全面的教程的地方,但是如果您在该站点上寻求有关特定问题的帮助,我相信社区会为您提供帮助。

PS:我假设您使用Linux是因为您曾经使用过ctypes.CDLL('libc.so.6')。如果您使用的是其他操作系统,则情况可能会有所改变(或相当多)。

Here’s a quick and dirty ctypes tutorial.

First, write your C library. Here’s a simple Hello world example:

testlib.c

#include <stdio.h>

void myprint(void);

void myprint()
{
    printf("hello world\n");
}

Now compile it as a shared library (mac fix found here):

$ gcc -shared -Wl,-soname,testlib -o testlib.so -fPIC testlib.c

# or... for Mac OS X 
$ gcc -shared -Wl,-install_name,testlib.so -o testlib.so -fPIC testlib.c

Then, write a wrapper using ctypes:

testlibwrapper.py

import ctypes

testlib = ctypes.CDLL('/full/path/to/testlib.so')
testlib.myprint()

Now execute it:

$ python testlibwrapper.py

And you should see the output

Hello world
$

If you already have a library in mind, you can skip the non-python part of the tutorial. Make sure ctypes can find the library by putting it in /usr/lib or another standard directory. If you do this, you don’t need to specify the full path when writing the wrapper. If you choose not to do this, you must provide the full path of the library when calling ctypes.CDLL().

This isn’t the place for a more comprehensive tutorial, but if you ask for help with specific problems on this site, I’m sure the community would help you out.

PS: I’m assuming you’re on Linux because you’ve used ctypes.CDLL('libc.so.6'). If you’re on another OS, things might change a little bit (or quite a lot).


回答 1

Chinmay Kanchi的答案很好,但是我想要一个传递和返回变量/数组到C ++代码的函数示例。我会在这里包括它,以防它对其他人有用。

传递并返回整数

该函数的C ++代码采用整数并将其加到返回值中,

extern "C" int add_one(int i)
{
    return i+1;
}

另存为文件test.cpp,注意所需的 extern“ C”(对于C代码可以删除)。这是使用g ++编译的,其参数类似于Chinmay Kanchi的答案,

g++ -shared -o testlib.so -fPIC test.cpp

Python代码使用load_librarynumpy.ctypeslib假定路径到与Python脚本位于同一目录中的共享库,

import numpy.ctypeslib as ctl
import ctypes

libname = 'testlib.so'
libdir = './'
lib=ctl.load_library(libname, libdir)

py_add_one = lib.add_one
py_add_one.argtypes = [ctypes.c_int]
value = 5
results = py_add_one(value)
print(results)

这将按预期打印6。

传递并打印数组

您还可以按以下方式传递数组,以使C代码可以打印数组的元素,

extern "C" void print_array(double* array, int N)
{
    for (int i=0; i<N; i++) 
        cout << i << " " << array[i] << endl;
}

与以前一样编译,并以相同方式导入。然后,使用该功能的额外Python代码将是,

import numpy as np

py_print_array = lib.print_array
py_print_array.argtypes = [ctl.ndpointer(np.float64, 
                                         flags='aligned, c_contiguous'), 
                           ctypes.c_int]
A = np.array([1.4,2.6,3.0], dtype=np.float64)
py_print_array(A, 3)

在这里我们指定数组,第一个参数print_array,作为指针对齐,c_contiguous 64个浮点的numpy的阵列,第二个参数为一个整数,它告诉C代码numpy的数组中的元素数目。然后通过C代码如下打印,

1.4
2.6
3.0

The answer by Chinmay Kanchi is excellent but I wanted an example of a function which passes and returns a variables/arrays to a C++ code. I though I’d include it here in case it is useful to others.

Passing and returning an integer

The C++ code for a function which takes an integer and adds one to the returned value,

extern "C" int add_one(int i)
{
    return i+1;
}

Saved as file test.cpp, note the required extern “C” (this can be removed for C code). This is compiled using g++, with arguments similar to Chinmay Kanchi answer,

g++ -shared -o testlib.so -fPIC test.cpp

The Python code uses load_library from the numpy.ctypeslib assuming the path to the shared library in the same directory as the Python script,

import numpy.ctypeslib as ctl
import ctypes

libname = 'testlib.so'
libdir = './'
lib=ctl.load_library(libname, libdir)

py_add_one = lib.add_one
py_add_one.argtypes = [ctypes.c_int]
value = 5
results = py_add_one(value)
print(results)

This prints 6 as expected.

Passing and printing an array

You can also pass arrays as follows, for a C code to print the element of an array,

extern "C" void print_array(double* array, int N)
{
    for (int i=0; i<N; i++) 
        cout << i << " " << array[i] << endl;
}

which is compiled as before and the imported in the same way. The extra Python code to use this function would then be,

import numpy as np

py_print_array = lib.print_array
py_print_array.argtypes = [ctl.ndpointer(np.float64, 
                                         flags='aligned, c_contiguous'), 
                           ctypes.c_int]
A = np.array([1.4,2.6,3.0], dtype=np.float64)
py_print_array(A, 3)

where we specify the array, the first argument to print_array, as a pointer to a Numpy array of aligned, c_contiguous 64 bit floats and the second argument as an integer which tells the C code the number of elements in the Numpy array. This then printed by the C code as follows,

1.4
2.6
3.0

回答 2

首先:>>>您在python示例中看到的代码是一种表明它是Python代码的方式。它用于将Python代码与输出分开。像这样:

>>> 4+5
9

在这里,我们看到以开头的行>>>是Python代码,其结果是9。这就是您启动Python解释器时的样子,这就是为什么这样做的原因。

您永远不会将>>>零件输入.py文件。

这样可以解决您的语法错误。

其次,ctypes只是包装Python库的几种方法之一。其他方法是 SWIG,它将查看您的Python库并生成一个公开C API的Python C扩展模块。另一种方法是使用Cython

它们都有优点和缺点。

SWIG只会将您的C API公开给Python。这意味着您没有任何对象或任何东西,您必须制作一个单独的Python文件来执行此操作。但是,通常有一个名为“ wowza”的模块和一个名为“ _wowza”的SWIG模块,该模块是C API的包装器。这是做事的好方法。

Cython生成一个C-扩展文件。这样做的好处是,您编写的所有Python代码都使用C语言编写,因此编写的对象也都使用C语言编写,这可以提高性能。但是您必须学习它与C的接口,因此要学习如何使用它还需要做一些额外的工作。

ctypes的优点是无需编译C代码,因此非常适合用于包装其他人编写的标准库,并且已经存在于Windows和OS X的二进制版本中。

Firstly: The >>> code you see in python examples is a way to indicate that it is Python code. It’s used to separate Python code from output. Like this:

>>> 4+5
9

Here we see that the line that starts with >>> is the Python code, and 9 is what it results in. This is exactly how it looks if you start a Python interpreter, which is why it’s done like that.

You never enter the >>> part into a .py file.

That takes care of your syntax error.

Secondly, ctypes is just one of several ways of wrapping Python libraries. Other ways are SWIG, which will look at your Python library and generate a Python C extension module that exposes the C API. Another way is to use Cython.

They all have benefits and drawbacks.

SWIG will only expose your C API to Python. That means you don’t get any objects or anything, you’ll have to make a separate Python file doing that. It is however common to have a module called say “wowza” and a SWIG module called “_wowza” that is the wrapper around the C API. This is a nice and easy way of doing things.

Cython generates a C-Extension file. It has the benefit that all of the Python code you write is made into C, so the objects you write are also in C, which can be a performance improvement. But you’ll have to learn how it interfaces with C so it’s a little bit extra work to learn how to use it.

ctypes have the benefit that there is no C-code to compile, so it’s very nice to use for wrapping standard libraries written by someone else, and already exists in binary versions for Windows and OS X.


在Python中包装C库:C,Cython或ctypes?

问题:在Python中包装C库:C,Cython或ctypes?

我想从Python应用程序调用C库。我不想包装整个API,只包装与我的情况相关的函数和数据类型。如我所见,我有三个选择:

  1. 在C中创建一个实际的扩展模块。可能有点过头了,我还想避免学习扩展编写的开销。
  2. 使用Cython将C库的相关部分公开给Python。
  3. 使用Python ctypes与外部库进行通信,从而完成整个工作。

我不确定2)还是3)是更好的选择。3)的优点是它ctypes是标准库的一部分,并且生成的代码将是纯Python –尽管我不确定该优点实际上有多大。

两种选择都有其他优点/缺点吗?您推荐哪种方法?


编辑:感谢您的所有答复,它们为希望做类似事情的任何人提供了很好的资源。当然,仍需针对单个案例做出决定-没有人会回答“这是对的”。就我自己而言,我可能会使用ctypes,但我也期待在其他项目中试用Cython。

由于没有一个单一的真实答案,因此接受一个答案有点武断。我选择了FogleBird的答案,因为它提供了对ctypes的一些很好的了解,并且它也是当前投票最高的答案。但是,我建议您阅读所有答案以获得一个很好的概述。

再次感谢。

I want to call a C library from a Python application. I don’t want to wrap the whole API, only the functions and datatypes that are relevant to my case. As I see it, I have three choices:

  1. Create an actual extension module in C. Probably overkill, and I’d also like to avoid the overhead of learning extension writing.
  2. Use Cython to expose the relevant parts from the C library to Python.
  3. Do the whole thing in Python, using ctypes to communicate with the external library.

I’m not sure whether 2) or 3) is the better choice. The advantage of 3) is that ctypes is part of the standard library, and the resulting code would be pure Python – although I’m not sure how big that advantage actually is.

Are there more advantages / disadvantages with either choice? Which approach do you recommend?


Edit: Thanks for all your answers, they provide a good resource for anyone looking to do something similar. The decision, of course, is still to be made for the single case—there’s no one “This is the right thing” sort of answer. For my own case, I’ll probably go with ctypes, but I’m also looking forward to trying out Cython in some other project.

With there being no single true answer, accepting one is somewhat arbitrary; I chose FogleBird’s answer as it provides some good insight into ctypes and it currently also is the highest-voted answer. However, I suggest to read all the answers to get a good overview.

Thanks again.


回答 0

ctypes 是快速完成它的最佳选择,并且在仍在编写Python的情况下很高兴与您合作!

我最近包装了一个FTDI驱动程序,用于使用ctypes与USB芯片进行通信,这很棒。我完成了所有工作,并在不到一个工作日的时间内完成了工作。(我只实现了我们需要的功能,大约有15个功能)。

出于同一目的,我们以前使用的是第三方模块PyUSB。PyUSB是实际的C / Python扩展模块。但是PyUSB在阻止读写时并没有释放GIL,这给我们带来了麻烦。因此,我使用ctypes编写了自己的模块,该模块在调用本机函数时会释放GIL。

需要注意的一件事是,ctypes不会知道#define所使用的库中的常数和内容,而仅是函数,因此您必须在自己的代码中重新定义这些常数。

这是一个代码最终的外观示例(很多内容被删除,只是试图向您展示其要旨):

from ctypes import *

d2xx = WinDLL('ftd2xx')

OK = 0
INVALID_HANDLE = 1
DEVICE_NOT_FOUND = 2
DEVICE_NOT_OPENED = 3

...

def openEx(serial):
    serial = create_string_buffer(serial)
    handle = c_int()
    if d2xx.FT_OpenEx(serial, OPEN_BY_SERIAL_NUMBER, byref(handle)) == OK:
        return Handle(handle.value)
    raise D2XXException

class Handle(object):
    def __init__(self, handle):
        self.handle = handle
    ...
    def read(self, bytes):
        buffer = create_string_buffer(bytes)
        count = c_int()
        if d2xx.FT_Read(self.handle, buffer, bytes, byref(count)) == OK:
            return buffer.raw[:count.value]
        raise D2XXException
    def write(self, data):
        buffer = create_string_buffer(data)
        count = c_int()
        bytes = len(data)
        if d2xx.FT_Write(self.handle, buffer, bytes, byref(count)) == OK:
            return count.value
        raise D2XXException

有人对各种选项做了一些基准测试

如果不得不包装带有许多类/模板/等的C ++库,我可能会更加犹豫。但是ctypes可以很好地与结构配合使用,甚至可以回调到Python中。

ctypes is your best bet for getting it done quickly, and it’s a pleasure to work with as you’re still writing Python!

I recently wrapped an FTDI driver for communicating with a USB chip using ctypes and it was great. I had it all done and working in less than one work day. (I only implemented the functions we needed, about 15 functions).

We were previously using a third-party module, PyUSB, for the same purpose. PyUSB is an actual C/Python extension module. But PyUSB wasn’t releasing the GIL when doing blocking reads/writes, which was causing problems for us. So I wrote our own module using ctypes, which does release the GIL when calling the native functions.

One thing to note is that ctypes won’t know about #define constants and stuff in the library you’re using, only the functions, so you’ll have to redefine those constants in your own code.

Here’s an example of how the code ended up looking (lots snipped out, just trying to show you the gist of it):

from ctypes import *

d2xx = WinDLL('ftd2xx')

OK = 0
INVALID_HANDLE = 1
DEVICE_NOT_FOUND = 2
DEVICE_NOT_OPENED = 3

...

def openEx(serial):
    serial = create_string_buffer(serial)
    handle = c_int()
    if d2xx.FT_OpenEx(serial, OPEN_BY_SERIAL_NUMBER, byref(handle)) == OK:
        return Handle(handle.value)
    raise D2XXException

class Handle(object):
    def __init__(self, handle):
        self.handle = handle
    ...
    def read(self, bytes):
        buffer = create_string_buffer(bytes)
        count = c_int()
        if d2xx.FT_Read(self.handle, buffer, bytes, byref(count)) == OK:
            return buffer.raw[:count.value]
        raise D2XXException
    def write(self, data):
        buffer = create_string_buffer(data)
        count = c_int()
        bytes = len(data)
        if d2xx.FT_Write(self.handle, buffer, bytes, byref(count)) == OK:
            return count.value
        raise D2XXException

Someone did some benchmarks on the various options.

I might be more hesitant if I had to wrap a C++ library with lots of classes/templates/etc. But ctypes works well with structs and can even callback into Python.


回答 1

警告:Cython核心开发人员的意见。

我几乎总是建议Cython胜过ctypes。原因是它具有更平滑的升级路径。如果使用ctypes,一开始很多事情都会很简单,用纯Python编写FFI代码当然很酷,而无需编译,构建依赖关系以及所有这些。但是,在某个时候,您几乎可以肯定会发现,您必须循环或以较长的一系列相互依赖的调用方式大量调用C库,并且您希望加快速度。在这一点上,您会注意到无法使用ctypes做到这一点。或者,当您需要回调函数并且发现Python回调代码成为瓶颈时,您也想加快它的速度和/或也将它移入C。同样,您不能使用ctypes做到这一点。

使用OTOH的Cython,您可以完全自由地使包装和调用代码变薄或变厚。您可以从常规Python代码对C代码的简单调用开始,然后Cython会将它们转换为本地C调用,而没有任何额外的调用开销,并且Python参数的转换开销非常低。当您发现需要对C库进行太多昂贵的调用时甚至需要更高的性能时,可以开始使用静态类型注释周围的Python代码,并让Cython为您直接对其进行优化。或者,您可以开始在Cython中重写部分C代码,以避免调用并在算法上专门化和加强循环。如果您需要快速回调,只需编写具有适当签名的函数,然后将其直接传递到C回调注册表即可。再次,没有开销,并且它为您提供了普通的C调用性能。而且在不太可能发生的情况下,您实际上无法在Cython中获得足够快的代码,您仍然可以考虑使用C(或C ++或Fortran)重写其真正关键的部分,并自然地从本地从Cython代码中调用它。但是,这实际上成了最后的选择,而不是唯一的选择。

因此,ctypes可以很好地完成简单的事情并快速使某些事情运行。但是,一旦事情开始发展,您很可能会发现您最好从一开始就使用Cython。

Warning: a Cython core developer’s opinion ahead.

I almost always recommend Cython over ctypes. The reason is that it has a much smoother upgrade path. If you use ctypes, many things will be simple at first, and it’s certainly cool to write your FFI code in plain Python, without compilation, build dependencies and all that. However, at some point, you will almost certainly find that you have to call into your C library a lot, either in a loop or in a longer series of interdependent calls, and you would like to speed that up. That’s the point where you’ll notice that you can’t do that with ctypes. Or, when you need callback functions and you find that your Python callback code becomes a bottleneck, you’d like to speed it up and/or move it down into C as well. Again, you cannot do that with ctypes. So you have to switch languages at that point and start rewriting parts of your code, potentially reverse engineering your Python/ctypes code into plain C, thus spoiling the whole benefit of writing your code in plain Python in the first place.

With Cython, OTOH, you’re completely free to make the wrapping and calling code as thin or thick as you want. You can start with simple calls into your C code from regular Python code, and Cython will translate them into native C calls, without any additional calling overhead, and with an extremely low conversion overhead for Python parameters. When you notice that you need even more performance at some point where you are making too many expensive calls into your C library, you can start annotating your surrounding Python code with static types and let Cython optimise it straight down into C for you. Or, you can start rewriting parts of your C code in Cython in order to avoid calls and to specialise and tighten your loops algorithmically. And if you need a fast callback, just write a function with the appropriate signature and pass it into the C callback registry directly. Again, no overhead, and it gives you plain C calling performance. And in the much less likely case that you really cannot get your code fast enough in Cython, you can still consider rewriting the truly critical parts of it in C (or C++ or Fortran) and call it from your Cython code naturally and natively. But then, this really becomes the last resort instead of the only option.

So, ctypes is nice to do simple things and to quickly get something running. However, as soon as things start to grow, you’ll most likely come to the point where you notice that you’d better used Cython right from the start.


回答 2

Cython本身就是一个很酷的工具,值得学习,并且令人惊讶地接近Python语法。如果您使用Numpy进行任何科学计算,那么Cython是必经之路,因为它与Numpy集成在一起可实现快速矩阵运算。

Cython是Python语言的超集。您可以向其抛出任何有效的Python文件,它将吐出有效的C程序。在这种情况下,Cython只会将Python调用映射到基础CPython API。由于不再解释您的代码,因此可能导致50%的加速。

为了进行一些优化,您必须开始告诉Cython有关代码的其他事实,例如类型声明。如果讲得足够多,它可以将代码简化为纯C。也就是说,Python中的for循环变成C中的for循环。在这里,您将看到大量的速度提升。您也可以在此处链接到外部C程序。

使用Cython代码也非常容易。我以为手册听起来很难。您实际上只是这样做:

$ cython mymodule.pyx
$ gcc [some arguments here] mymodule.c -o mymodule.so

然后您可以import mymodule在您的Python代码中完全忘记了它可以编译为C。

无论如何,由于Cython易于安装和开始使用,因此建议您尝试一下它是否适合您的需求。如果事实证明不是您要寻找的工具,那将不是浪费。

Cython is a pretty cool tool in itself, well worth learning, and is surprisingly close to the Python syntax. If you do any scientific computing with Numpy, then Cython is the way to go because it integrates with Numpy for fast matrix operations.

Cython is a superset of Python language. You can throw any valid Python file at it, and it will spit out a valid C program. In this case, Cython will just map the Python calls to the underlying CPython API. This results in perhaps a 50% speedup because your code is no longer interpreted.

To get some optimizations, you have to start telling Cython additional facts about your code, such as type declarations. If you tell it enough, it can boil the code down to pure C. That is, a for loop in Python becomes a for loop in C. Here you will see massive speed gains. You can also link to external C programs here.

Using Cython code is also incredibly easy. I thought the manual makes it sound difficult. You literally just do:

$ cython mymodule.pyx
$ gcc [some arguments here] mymodule.c -o mymodule.so

and then you can import mymodule in your Python code and forget entirely that it compiles down to C.

In any case, because Cython is so easy to setup and start using, I suggest trying it to see if it suits your needs. It won’t be a waste if it turns out not to be the tool you’re looking for.


回答 3

为了从Python应用程序调用C库,还有cffi,它是ctypes的新选择。它为FFI带来了全新外观:

  • 它以一种引人入胜,干净的方式处理问题(与ctypes相对
  • 它不需要编写非Python代码(如SWIG,Cython等)

For calling a C library from a Python application there is also cffi which is a new alternative for ctypes. It brings a fresh look for FFI:

  • it handles the problem in a fascinating, clean way (as opposed to ctypes)
  • it doesn’t require to write non Python code (as in SWIG, Cython, …)

回答 4

我再扔一个:SWIG

它易于学习,可以正确完成许多事情,并且支持更多语言,因此花时间学习它会非常有用。

如果您使用SWIG,则将创建一个新的python扩展模块,但是SWIG将为您完成大部分繁重的工作。

I’ll throw another one out there: SWIG

It’s easy to learn, does a lot of things right, and supports many more languages so the time spent learning it can be pretty useful.

If you use SWIG, you are creating a new python extension module, but with SWIG doing most of the heavy lifting for you.


回答 5

就个人而言,我会用C语言编写一个扩展模块。不要被Python C扩展吓到了-它们一点也不难编写。该文档非常清楚并且很有帮助。当我第一次用Python编写C扩展时,我认为花了大约一个小时才弄清楚如何编写一个-根本没有多少时间。

Personally, I’d write an extension module in C. Don’t be intimidated by Python C extensions — they’re not hard at all to write. The documentation is very clear and helpful. When I first wrote a C extension in Python, I think it took me about an hour to figure out how to write one — not much time at all.


回答 6

当您已经有编译的库blob要处理时(例如OS库),ctypes很棒。但是,调用开销很大,因此,如果您要在库中进行大量调用,并且无论如何都要编写C代码(或至少要编译它),我会说赛顿。它不需要太多工作,并且使用生成的pyd文件会更快,更pythonic。

我个人倾向于使用cython来加快python代码的速度(循环和整数比较是cython尤为突出的两个领域),并且当涉及到更多涉及代码/其他库的包装时,我将转向Boost.Python。。Boost.Python的设置可能很繁琐,但是一旦它开始工作,它将使包装C / C ++代码变得简单。

cython也非常擅长包装numpy(这是我从SciPy 2009程序中了解到的),但是我没有使用numpy,所以我无法对此发表评论。

ctypes is great when you’ve already got a compiled library blob to deal with (such as OS libraries). The calling overhead is severe, however, so if you’ll be making a lot of calls into the library, and you’re going to be writing the C code anyway (or at least compiling it), I’d say to go for cython. It’s not much more work, and it’ll be much faster and more pythonic to use the resulting pyd file.

I personally tend to use cython for quick speedups of python code (loops and integer comparisons are two areas where cython particularly shines), and when there is some more involved code/wrapping of other libraries involved, I’ll turn to Boost.Python. Boost.Python can be finicky to set up, but once you’ve got it working, it makes wrapping C/C++ code straightforward.

cython is also great at wrapping numpy (which I learned from the SciPy 2009 proceedings), but I haven’t used numpy, so I can’t comment on that.


回答 7

如果您已经有一个带有定义的API的库,我认为 ctypes是最好的选择,因为您只需要进行一些初始化,然后或多或少以您习惯的方式调用该库。

我认为Cython或用C创建扩展模块(这不是很困难)在需要新代码时更有用,例如,调用该库并执行一些复杂,耗时的任务,然后将结果传递给Python。

对于简单程序,另一种方法是直接执行不同的过程(在外部编译),将结果输出到标准输出,并使用子过程模块进行调用。有时,这是最简单的方法。

例如,如果您使控制台C程序或多或少地以这种方式工作

$miCcode 10
Result: 12345678

您可以从Python调用它

>>> import subprocess
>>> p = subprocess.Popen(['miCcode', '10'], shell=True, stdout=subprocess.PIPE)
>>> std_out, std_err = p.communicate()
>>> print std_out
Result: 12345678

只需一点点字符串格式化,您就可以按照您想要的任何方式获取结果。您还可以捕获标准错误输出,因此非常灵活。

If you have already a library with a defined API, I think ctypes is the best option, as you only have to do a little initialization and then more or less call the library the way you’re used to.

I think Cython or creating an extension module in C (which is not very difficult) are more useful when you need new code, e.g. calling that library and do some complex, time-consuming tasks, and then passing the result to Python.

Another approach, for simple programs, is directly do a different process (compiled externally), outputting the result to standard output and call it with subprocess module. Sometimes it’s the easiest approach.

For example, if you make a console C program that works more or less that way

$miCcode 10
Result: 12345678

You could call it from Python

>>> import subprocess
>>> p = subprocess.Popen(['miCcode', '10'], shell=True, stdout=subprocess.PIPE)
>>> std_out, std_err = p.communicate()
>>> print std_out
Result: 12345678

With a little string formating, you can take the result in any way you want. You can also capture the standard error output, so it’s quite flexible.


回答 8

有一个问题使我使用ctypes而不是cython,其他答案中未提及。

使用ctypes的结果根本不取决于您使用的编译器。您可以或多或少地使用可以编译为本机共享库的任何语言来编写库。哪种系统,哪种语言和哪种编译器都没关系。但是,Cython受基础架构的限制。例如,如果您想在Windows上使用Intel编译器,则使cython正常工作要困难得多:您应将cython解释为cython,使用此精确的编译器重新编译某些内容,等等。这大大限制了可移植性。

There is one issue which made me use ctypes and not cython and which is not mentioned in other answers.

Using ctypes the result does not depend on compiler you are using at all. You may write a library using more or less any language which may be compiled to native shared library. It does not matter much, which system, which language and which compiler. Cython, however, is limited by the infrastructure. E.g, if you want to use intel compiler on windows, it is much more tricky to make cython work: you should “explain” compiler to cython, recompile something with this exact compiler, etc. Which significantly limits portability.


回答 9

如果您以Windows为目标并且选择包装一些专有的C ++库,那么您很快就会发现msvcrt***.dll(Visual C ++ Runtime)的不同版本略有不兼容。

这意味着您可能无法使用,Cython因为结果wrapper.pyd链接到msvcr90.dll (Python 2.7)msvcr100.dll (Python 3.x)。如果要包装的库是针对不同版本的运行时链接的,那么您就不走运了。

然后,要使工作正常,您需要为C ++库创建C包装程序,将该包装程序dll链接到与msvcrt***.dllC ++库相同的版本。然后用于ctypes在运行时动态加载手动包装的dll。

因此,有很多小细节,下面的文章中将对其进行详细描述:

“美丽的本地库(使用Python) ”:http : //lucumr.pocoo.org/2013/8/18/beautiful-native-libraries/

If you are targeting Windows and choose to wrap some proprietary C++ libraries, then you may soon discover that different versions of msvcrt***.dll (Visual C++ Runtime) are slightly incompatible.

This means that you may not be able to use Cython since resulting wrapper.pyd is linked against msvcr90.dll (Python 2.7) or msvcr100.dll (Python 3.x). If the library that you are wrapping is linked against different version of runtime, then you’re out of luck.

Then to make things work you’ll need to create C wrappers for C++ libraries, link that wrapper dll against the same version of msvcrt***.dll as your C++ library. And then use ctypes to load your hand-rolled wrapper dll dynamically at the runtime.

So there are lots of small details, which are described in great detail in following article:

“Beautiful Native Libraries (in Python)“: http://lucumr.pocoo.org/2013/8/18/beautiful-native-libraries/


回答 10

对于使用GLib的库,也有可能使用GObject Introspection

There’s also one possibility to use GObject Introspection for libraries that are using GLib.


回答 11

我知道这是一个老问题,但是当您搜索诸如之类的东西时,这件事就会出现在Google上ctypes vs cython,并且这里的大多数答案都是由精通这些知识的人编写的,cython或者c可能无法反映您学习这些所需要的实际时间实施您的解决方案。我都是这方面的初学者。我以前从未接触过cython,并且经验很少c/c++

在过去的两天里,我一直在寻找一种方法来将代码中性能很重要的部分委托给比python更底层的东西。我在ctypes和中都实现了我的代码Cython,该代码基本上由两个简单的函数组成。

我有一个庞大的字符串列表需要处理。注意liststring。这两种类型都不完全对应于中的类型c,因为默认情况下python字符串是unicode,而c字符串不是。python中的列表根本不是c的数组。

这是我的判决。使用cython。它与python的集成更加流畅,并且通常更易于使用。当出现问题时,ctypes只会引发段错误,至少cython会在可能的情况下为您提供带有堆栈跟踪的编译警告,并且您可以使用轻松返回有效的python对象cython

这是有关我需要花多少时间来实现这两个功能的详细说明。顺便说一下,我很少进行C / C ++编程:

  • C类型:

    • 关于研究如何将unicode字符串列表转换为ac兼容类型的大约2小时。
    • 关于如何从ac函数正确返回字符串的大约一个小时。在编写函数之后,实际上我在这里为SO提供了自己的解决方案。
    • 用c编写代码大约半小时,然后将其编译到动态库中。
    • 10分钟在python中编写测试代码以检查c代码是否有效。
    • 做一些测试并重新排列c代码大约一个小时。
    • 然后,我将c代码插入到实际的代码库中,发现该模块ctypes不能很好地与multiprocessing模块配合使用,因为默认情况下无法选择其处理程序。
    • 大约20分钟后,我重新排列了代码以不使用multiprocessing模块,然后重试。
    • 然后,c尽管我的代码中的第二个功能通过了我的测试代码,但仍在我的代码库中产生了段错误。好吧,这可能是我不能很好地检查边缘情况的错,我一直在寻找一种快速的解决方案。
    • 在大约40分钟的时间里,我试图确定这些段错误的可能原因。
    • 我将函数分为两个库,然后重试。我的第二个功能仍然存在段错误。
    • 我决定放开第二个函数,只使用c代码的第一个函数,并且在使用它的python循环的第二或第三次迭代中,UnicodeError尽管我进行了一切编码和解码,但我仍未在某个位置解码字节明确地

在这一点上,我决定寻找一种替代方法,并决定研究cython

  • 赛顿
    • 阅读cython hello world的 10分钟。
    • 15分钟内检查SO如何使用cython setuptools代替distutils
    • 关于cython类型和python类型的阅读,需要 10分钟。我了解到我可以使用大多数内置的python类型进行静态输入。
    • 用cython类型重新注释我的python代码的15分钟。
    • 修改setup.py我的代码库中使用编译模块的10分钟。
    • 将模块直接插入multiprocessing代码库的版本。有用。

根据记录,我当然没有衡量投资的确切时机。很可能是由于我在处理ctypes时需要付出精神上的努力,所以我对时间的感觉有些不专心。但是,应该传达处理的手感cythonctypes

I know this is an old question but this thing comes up on google when you search stuff like ctypes vs cython, and most of the answers here are written by those who are proficient already in cython or c which might not reflect the actual time you needed to invest to learn those to implement your solution. I am a complete beginner in both. I have never touched cython before, and have very little experience on c/c++.

For the last two days, I was looking for a way to delegate a performance heavy part of my code to something more low level than python. I implemented my code both in ctypes and Cython, which consisted basically of two simple functions.

I had a huge string list that needed to processed. Notice list and string. Both types do not correspond perfectly to types in c, because python strings are by default unicode and c strings are not. Lists in python are simply NOT arrays of c.

Here is my verdict. Use cython. It integrates more fluently to python, and easier to work with in general. When something goes wrong ctypes just throws you segfault, at least cython will give you compile warnings with a stack trace whenever it is possible, and you can return a valid python object easily with cython.

Here is a detailed account on how much time I needed to invest in both them to implement the same function. I did very little C/C++ programming by the way:

  • Ctypes:

    • About 2h on researching how to transform my list of unicode strings to a c compatible type.
    • About an hour on how to return a string properly from a c function. Here I actually provided my own solution to SO once I have written the functions.
    • About half an hour to write the code in c, compile it to a dynamic library.
    • 10 minutes to write a test code in python to check if c code works.
    • About an hour of doing some tests and rearranging the c code.
    • Then I plugged the c code into actual code base, and saw that ctypes does not play well with multiprocessing module as its handler is not pickable by default.
    • About 20 minutes I rearranged my code to not use multiprocessing module, and retried.
    • Then second function in my c code generated segfaults in my code base although it passed my testing code. Well, this is probably my fault for not checking well with edge cases, I was looking for a quick solution.
    • For about 40 minutes I tried to determine possible causes of these segfaults.
    • I split my functions into two libraries and tried again. Still had segfaults for my second function.
    • I decided to let go of the second function and use only the first function of c code and at the second or third iteration of the python loop that uses it, I had a UnicodeError about not decoding a byte at the some position though I encoded and decoded everthing explicitely.

At this point, I decided to search for an alternative and decided to look into cython:

  • Cython
    • 10 min of reading cython hello world.
    • 15 min of checking SO on how to use cython with setuptools instead of distutils.
    • 10 min of reading on cython types and python types. I learnt I can use most of the builtin python types for static typing.
    • 15 min of reannotating my python code with cython types.
    • 10 min of modifying my setup.py to use compiled module in my codebase.
    • Plugged in the module directly to the multiprocessing version of codebase. It works.

For the record, I of course, did not measure the exact timings of my investment. It may very well be the case that my perception of time was a little to attentive due to mental effort required while I was dealing with ctypes. But it should convey the feel of dealing with cython and ctypes