Python以其开发简单、快捷和良好的生态受到全世界众多开发者的喜爱,但Python也因其较慢的运行速度被很多人诟病。同时,C语言以其能直接控制底层资源、运行速度快而广泛被用于操作系统和嵌入式设备的相关开发中,但C语言的缺点就是开发效率较低。那是否可以将两者结合起来?答案是可以的。关于Python调用C代码有好几种方式,在这里只举例介绍我个人常用且较为简单的使用Python的ctypes库调用C语言动态链接库的方法

实例:使用Python调用C语言编写的累加函数

C代码

1
2
3
4
5
6
7
8
9
10
11
12
// add.c(输入一个整数并返回1+2+3+...+num的值)
#include<stdio.h>

int add(long num)
{
long result = 0;
for(long i=1; i<=num; i++)
{
result += i;
}
return result;
}

C代码需要通过编译生成动态链接库文件(Linux系统下为.so文件,Windows系统下为.dll文件),这里以Linux为例

1
gcc -fPIC -O3 -shared add.c -o add.so

Python代码

使用Python调用C语言动态链接库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import time
from ctypes import *

def num_add_up(num: int) -> None:

result = 0
start = time.time()
lib = CDLL('./add.so')
lib.add.argtypes = [c_long] # 定义传入参数的类型
lib.add.restype = c_long # 定义返回值的类型
result = lib.add(num)
print(result)
print(time.time()-start)

if __name__ == '__main__':
num_add_up(100000000)

输出:
5000000050000000
0.03809380531311035

使用Python实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import time

def num_add_up(num: int) -> None:

result = 0
start = time.time()
for i in range(0, num+1):
result += i
print(result)
print(time.time()-start)

if __name__ == "__main__":
num_add_up(100000000)

输出:
5000000050000000
5.233763933181763

可以看出采用动态链接库的代码速度优势明显,可以通过这种方式对Python程序进行局部优化,提高程序的运行速度

ctypes参数类型

使用C语言动态链接库与Python的参数传递时,是无法直接传递Python中的变量的。需要ctypes进行相关的封装,ctypes在此定义了一些和C兼容的基本数据类型以供使用

ctypes 类型 C 类型 Python 类型
c_bool _Bool bool (1)
c_char char 单字符字节串对象
c_wchar wchar_t 单字符字符串
c_byte char int
c_ubyte unsigned char int
c_short short int
c_ushort unsigned short int
c_int int int
c_uint unsigned int int
c_long long int
c_ulong unsigned long int
c_longlong __int64 or long long int
c_ulonglong unsigned __int64 or unsigned long long int
c_size_t size_t int
c_ssize_t ssize_t or Py_ssize_t int
c_float float float
c_double double float
c_longdouble long double float
c_char_p char* (NUL terminated) 字节串对象或 None
c_wchar_p wchar_t* (NUL terminated) 字符串或 None
c_void_p void* int 或 None