在 Python 中调用 .so
(共享库,Linux 下的动态链接库)文件中的方法,主要依赖于 Python 的标准库 ctypes
。ctypes
提供了与 C 语言兼容的数据类型和函数,允许 Python 代码直接调用动态链接库中的函数。本文介绍详细的步骤和示例。
1. 准备 .so 文件
首先,需要一个 .so
文件。通常,这个文件是用 C 或 C++ 编写并编译而成的。
示例 C 代码 (mylib.c
):
#include <stdio.h>
// 一个简单的加法函数
int add(int a, int b) {
return a + b;
}
// 一个打印问候语的函数
void greet(const char* name) {
printf("Hello, %s!\n", name);
}
编译 C 代码生成 .so 文件:
在 Linux 系统中,可以使用 GCC 编译上述代码:
gcc -shared -o mylib.so mylib.c
这会生成一个名为 mylib.so
的共享库文件。
2. 使用 ctypes
调用 .so 文件
接下来,我们将使用 Python 的 ctypes
库来加载并调用 mylib.so
中的函数。
import ctypes
import os
# 1. 加载 .so 文件
# 确保 mylib.so 文件与你的 Python 脚本在同一个目录下,
# 或者提供完整的路径。
current_dir = os.path.dirname(os.path.abspath(__file__))
lib_path = os.path.join(current_dir, 'mylib.so')
try:
my_lib = ctypes.CDLL(lib_path)
except OSError as e:
print(f"Error loading library: {e}")
print("Please ensure 'mylib.so' is in the same directory as this script or provide the full path.")
exit()
# 2. 调用 add 函数
# 默认情况下,ctypes 假定函数返回 int 类型,并且参数是 int 类型。
# 但是,为了明确和避免潜在错误,最好显式指定参数类型和返回类型。
# 设置 add 函数的参数类型和返回类型
my_lib.add.argtypes = [ctypes.c_int, ctypes.c_int]
my_lib.add.restype = ctypes.c_int
result = my_lib.add(5, 3)
print(f"5 + 3 = {result}") # 输出: 5 + 3 = 8
# 3. 调用 greet 函数
# greet 函数接受一个字符串(C 中的 const char*)作为参数,并且没有返回值 (void)。
# 设置 greet 函数的参数类型和返回类型
# 对于字符串参数,使用 ctypes.c_char_p
# 对于 void 返回值,restype 不需要设置或设置为 None
my_lib.greet.argtypes = [ctypes.c_char_p]
my_lib.greet.restype = None # 或者不设置这行
# Python 字符串需要编码为字节串 (bytes) 才能传递给 C 函数
name = "Pythonista"
my_lib.greet(name.encode('utf-8')) # 输出: Hello, Pythonista!
# 尝试调用不存在的函数(会报错)
# try:
# my_lib.non_existent_function()
# except AttributeError as e:
# print(f"\nError: {e}")
关键概念和注意事项:
ctypes.CDLL
或 ctypes.CDLL
(Windows): 用于加载共享库。
ctypes.CDLL
用于调用遵循标准 C 调用约定(__cdecl
)的库。
ctypes.CDLL
用于调用遵循 __stdcall
调用约定的库(在 Windows 上常见)。
argtypes
: 一个列表,用于指定 C 函数的参数类型。Python 会自动将 Python 类型转换为相应的 C 类型。
ctypes.c_int
: C int
ctypes.c_float
: C float
ctypes.c_double
: C double
ctypes.c_char_p
: C char*
(字符串,需要传入字节串)
ctypes.c_void_p
: C void*
(通用指针)
ctypes.POINTER(ctypes.c_int)
: 指向 int
的指针
- 更多类型请参考
ctypes
文档。
restype
: 指定 C 函数的返回值类型。如果不设置,ctypes
默认假定返回 int
。如果 C 函数返回 void
,可以将其设置为 None
或不设置。
- 字符串处理: C 函数通常期望
char*
或 const char*
类型的字符串。在 Python 中,你需要将字符串编码为字节串 (.encode('utf-8')
) 传递给 C 函数。
- 错误处理: 如果
.so
文件不存在或路径不正确,ctypes.CDLL
会抛出 OSError
。如果调用的函数不存在,会抛出 AttributeError
。
- 复杂数据结构: 对于更复杂的数据结构(如结构体、数组),
ctypes
提供了 ctypes.Structure
、ctypes.Union
和数组类型 (ctypes.c_int * 10
) 来进行映射。
通过 ctypes
,Python 可以有效地与用其他语言编写的动态链接库进行交互,从而扩展其功能或利用现有高性能库。