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.