Python调用C:一次跨界的完美碰撞
在编程的世界里,Python以其简洁易懂的语法和强大的生态系统深受程序员的喜爱。然而,当我们谈到性能时,Python并不是最快的。有时候,我们需要在Python中调用C代码,来提升性能或利用现有的C库。本文将带你深入了解Python调用C的各种方法,从基础到高级,一步步揭开跨界的奥秘。
为什么要调用C?
在开始之前,我们先来聊聊为什么需要在Python中调用C。主要有以下几个原因:
- 性能提升:C语言以其高效的执行速度著称。对于一些计算密集型的任务,使用C代码可以显著提升性能。
- 利用现有库:许多成熟的库和框架都是用C或C++编写的,如OpenCV、NumPy等。通过调用这些库,可以避免重复造轮子。
- 资源访问:某些低级别的系统资源(如硬件接口)只能通过C来访问。
方法一:ctypes
什么是ctypes?
ctypes
是Python标准库中的一个模块,用于调用C函数和使用C数据类型。它允许你在Python中直接调用动态链接库(DLL)或共享对象(.so)中的函数。
基本用法
假设我们有一个C函数 add
,它接受两个整数参数并返回它们的和。我们可以将这个函数编译成一个动态链接库,然后在Python中调用它。
C代码(add.c)
1 2 3 4 5
| #include <stdio.h>
int add(int a, int b) { return a + b; }
|
编译成动态链接库
在Linux上,可以使用以下命令编译:
1
| gcc -shared -o libadd.so -fPIC add.c
|
在Windows上,可以使用以下命令编译:
1
| gcc -shared -o add.dll add.c
|
Python代码
1 2 3 4 5 6 7 8 9 10 11 12 13
| import ctypes
lib = ctypes.CDLL('./libadd.so')
lib.add.argtypes = (ctypes.c_int, ctypes.c_int) lib.add.restype = ctypes.c_int
result = lib.add(3, 5) print(f"3 + 5 = {result}")
|
高级用法
ctypes
还支持更复杂的数据类型和函数调用。例如,传递结构体、数组等。
C代码(struct_example.c)
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include <stdio.h>
typedef struct { int x; int y; } Point;
Point create_point(int x, int y) { Point p; p.x = x; p.y = y; return p; }
|
编译成动态链接库
1
| gcc -shared -o libstruct_example.so -fPIC struct_example.c
|
Python代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import ctypes
class Point(ctypes.Structure): _fields_ = [("x", ctypes.c_int), ("y", ctypes.c_int)]
lib = ctypes.CDLL('./libstruct_example.so')
lib.create_point.argtypes = (ctypes.c_int, ctypes.c_int) lib.create_point.restype = Point
point = lib.create_point(10, 20) print(f"Point: ({point.x}, {point.y})")
|
方法二:CFFI
什么是CFFI?
cffi
(C Foreign Function Interface)是一个更现代的库,用于在Python中调用C代码。它比 ctypes
更强大,支持更复杂的用法,如直接在Python中编写C代码。
基本用法
安装cffi
Python代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import cffi
ffi = cffi.FFI()
ffi.cdef(""" int add(int a, int b); """)
C = ffi.dlopen("./libadd.so")
result = C.add(3, 5) print(f"3 + 5 = {result}")
|
高级用法
cffi
支持直接在Python中编写C代码,这对于快速原型开发非常有用。
Python代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| import cffi
ffi = cffi.FFI()
ffi.cdef(""" int add(int a, int b); """)
ffi.set_source("_add", """ int add(int a, int b) { return a + b; } """)
ffi.compile()
from _add import lib
result = lib.add(3, 5) print(f"3 + 5 = {result}")
|
方法三:Cython
什么是Cython?
Cython
是一种将Python代码编译成C代码的工具,生成的C代码可以编译成动态链接库,然后在Python中调用。Cython不仅支持调用C代码,还支持编写混合Python和C的代码,以获得更好的性能。
基本用法
安装Cython
编写Cython代码(add.pyx)
1 2
| cdef int add(int a, int b): return a + b
|
编写设置文件(setup.py)
1 2 3 4 5 6
| from setuptools import setup from Cython.Build import cythonize
setup( ext_modules=cythonize("add.pyx") )
|
编译Cython代码
1
| python setup.py build_ext --inplace
|
Python代码
1 2 3 4
| import add
result = add.add(3, 5) print(f"3 + 5 = {result}")
|
高级用法
Cython 支持更复杂的用法,如类型注解、C结构体和数组等。
编写Cython代码(struct_example.pyx)
1 2 3 4 5 6 7 8 9
| cdef extern from "struct_example.h": ctypedef struct Point: int x int y Point create_point(int x, int y)
def create_point(int x, int y): cdef Point p = create_point(x, y) return p.x, p.y
|
编写头文件(struct_example.h)
1 2 3 4 5 6
| typedef struct { int x; int y; } Point;
Point create_point(int x, int y);
|
编写C代码(struct_example.c)
1 2 3 4 5 6 7 8
| #include "struct_example.h"
Point create_point(int x, int y) { Point p; p.x = x; p.y = y; return p; }
|
编写设置文件(setup.py)
1 2 3 4 5 6 7 8 9
| from setuptools import setup from Cython.Build import cythonize
setup( ext_modules=cythonize("struct_example.pyx"), include_dirs=["."], libraries=["struct_example"], library_dirs=["."], )
|
编译Cython代码
1 2 3
| gcc -c -fPIC struct_example.c -o struct_example.o gcc -shared struct_example.o -o libstruct_example.so python setup.py build_ext --inplace
|
Python代码
1 2 3 4
| import struct_example
x, y = struct_example.create_point(10, 20) print(f"Point: ({x}, {y})")
|
总结
在Python中调用C代码有多种方法,每种方法都有其适用的场景和优缺点。ctypes
简单易用,适合简单的函数调用;cffi
功能强大,支持更复杂的用法;Cython
则提供了混合Python和C的编写能力,适用于性能要求较高的场景。
希望本文能帮助你在Python中更高效地调用C代码。如果你有任何问题或建议,欢迎在评论区留言交流!
参考链接
祝你在编程的道路上越走越远,技术越来越牛!🚀