Python、PyPy 和 CFFI:我应该使用什么?

Python, PyPy and CFFI: what am I supposed to use?

我需要用 python 调用 C 库,经过一番考虑后,CFFI 似乎最适合这种工作。然而,到现在为止,如果我正确使用它,我会感到非常困惑,因为有些东西似乎只在 PyPy 上按预期工作,而另一些似乎只在 Python3 上工作(PyPy 不支持,因为据我所知)。

这是来自 CFFI 文档的最基本的代码示例:

>>> from cffi import FFI
>>> ffi = FFI()
>>> ffi.cdef("""
...     int printf(const char *format, ...);   // copy-pasted from the man page
... """)
>>> C = ffi.dlopen(None)                     # loads the entire C namespace
>>> arg = ffi.new("char[]", "world")         # equivalent to C code: char arg[] = "world";
>>> C.printf("hi there, %s.\n", arg)         # call printf
hi there, world.
17                                           # this is the return value
>>>

当 运行 将此代码与 Python3 结合使用时,出现以下错误: 类型错误:ctype 'char[]' 的初始值设定项必须是字节或列表或元组,而不是 str

查找错误,我发现它是去年 1 月在 PyPy 中修复的问题。所以我看看 PyPy 是否 运行 是这样,确实如此。万岁!

但是,在第二个示例中,我遇到了相反的问题:

# file "simple_example_build.py"

# Note: this particular example fails before version 1.0.2
# because it combines variadic function and ABI level.

from cffi import FFI

ffi = FFI()
ffi.set_source("_simple_example", None)
ffi.cdef("""
    int printf(const char *format, ...);
""")

if __name__ == "__main__":
    ffi.compile()

运行 PyPy 中的这个向我抛出另一个错误:

AttributeError: 'FFI' object has no attribute 'set_source'

由于示例说明它不适用于旧版本,因此我检查了我的 CFFI 版本:1.2.1,一切正常。

最后我 运行 使用 Python3 而不是 PyPy 的第二个例子,谁会想到,它确实做了它应该做的事情。

作为 Python 的新手,到现在为止我真的不知道我应该使用什么,以及为什么同一文档中的示例仅 运行 用于不同版本的语言。当然还有一个问题,我可能只是配置了一些错误(对 Linux 也是新手),或者我应该完全使用另一个 python 版本。有人可以阐明这一点吗?

When running this code with Python3, I get the following error: TypeError: initializer for ctype 'char[]' must be a bytes or list or tuple, not str

是的,因为对于 Python 3,您需要使用 'b' 前缀来确保您正在处理字节。文档中的示例在下面清楚地说明了这一点。

AttributeError: 'FFI' object has no attribute 'set_source'

这意味着您使用的是旧版本的 PyPy。无法在给定版本的 PyPy 中升级 CFFI 的版本;你需要升级 PyPy 本身。像这样检查您的特定 PyPy 附带的 CFFI 版本:

$ pypy
Python 2.7.9 (295ee98b6928, May 31 2015, 07:29:04)
[PyPy 2.6.0 with GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>> import cffi
>>>> cffi.__version__
'1.1.0'

我很确定您使用的是旧版 CFFI 附带的旧版 PyPy,而不是 1.2.1。

您可以修改如下:

from cffi import FFI
ffi = FFI()
ffi.cdef("""int printf(const char *format, ...);""")
C = ffi.dlopen(None)                     # loads the entire C namespace
arg = ffi.new("char[]", b"world")         # equivalent to C code: char arg[]     = "world";
C.printf(b"hi there, %s.\n", arg)         # call printf