Ctypes

ZaynPei Lv6

ctypes 的全称是 C types foreign function library for Python。它的主要作用是在 Python 中加载并调用 C 语言动态链接库(Shared Library),并在两种语言之间传递数据, 实现 Python 与底层高性能代码的无缝结合。

基本用法结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import ctypes

# 1️⃣ 加载动态库
lib = ctypes.CDLL("./mylib.so") # Linux
# 或:ctypes.WinDLL("mylib.dll") # Windows
# 或:ctypes.CDLL("./mylib.dylib") # macOS

# 2️⃣ 声明函数参数类型和返回值类型
lib.add.argtypes = [ctypes.c_double, ctypes.c_double] # 声明add函数的参数类型
lib.add.restype = ctypes.c_double # 声明add函数的返回值类型

# 3️⃣ 调用函数
result = lib.add(1.5, 2.3)
print(result) # 输出 3.8

主要功能分类

加载动态库 ctypes 提供了多种函数来加载不同类型的动态库,常用的有:

函数 说明 ctypes.CDLL(path) 加载普通 C 动态库(标准调用约定) ctypes.WinDLL(path) Windows 专用,适用于 stdcall 调用约定 ctypes.PyDLL(path) Python 扩展库加载(保留 GIL)

常见的 C 类型映射 C 类型 Python 映射 int ctypes.c_int float ctypes.c_float double ctypes.c_double char ctypes.c_char char* ctypes.c_char_p void* ctypes.c_void_p bool ctypes.c_bool struct 自定义 ctypes.Structure 子类

传递数组 C 语言中常常需要传递数组指针, 可以用 ctypes 的 POINTER 或 Array 来完成。 POINTER的作用是创建一个指向某种类型的指针类型。

1
2
3
4
5
6
7
8
9
10
11
import ctypes

# 创建一个 double 数组
DoubleArray = ctypes.c_double * 5
arr = DoubleArray(1.0, 2.0, 3.0, 4.0, 5.0)

# 假设 C 函数定义:double mean(double* arr, int n)
lib.mean.argtypes = [ctypes.POINTER(ctypes.c_double), ctypes.c_int]
lib.mean.restype = ctypes.c_double

print(lib.mean(arr, 5))

除此之外, 还可以直接用 NumPy 数组的 .ctypes 接口拿到指针:

1
2
3
4
5
import numpy as np

arr = np.arange(10, dtype=np.double)
ptr = arr.ctypes.data_as(ctypes.POINTER(ctypes.c_double))
lib.mean(ptr, len(arr))

示例

下面是一个完整的示例,展示了如何用 ctypes 调用一个简单的 C 动态库函数。

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# Python 端代码

import ctypes
import numpy as np

# 加载C++编译的动态库
# 假设 'fast_indicators.so' 是由 C++ 源码编译而来的
lib = ctypes.CDLL('./fast_indicators.so')

# 声明C++函数接口
# 告诉 Python,C++ 中的 calculate_sma 函数的参数类型
lib.calculate_sma.argtypes = [
ctypes.POINTER(ctypes.c_double), # prices (价格数组指针)
ctypes.c_int, # length (数组长度)
ctypes.c_int, # period (移动平均周期)
ctypes.POINTER(ctypes.c_double) # result (结果数组指针)
]

def fast_sma(prices, period):
"""使用C++实现的快速移动平均"""

# 1. 将 Python 数据转换为 C++ 兼容的类型
prices_arr = (ctypes.c_double * len(prices))(*prices)
result_arr = (ctypes.c_double * len(prices))() # 创建一个空的 C 类型数组来接收结果

# 2. 调用 C++ 函数
lib.calculate_sma(prices_arr, len(prices), period, result_arr)

# 3. 将 C++ 返回的结果转换回 Python 的 numpy 数组
return np.array(result_arr)

# 在Python策略中调用
class HybridStrategy:
def __init__(self):
self.prices = []

def on_tick(self, price): # 模拟接收行情数据
self.prices.append(price) # 收集价格数据

if len(self.prices) > 100:
# 用 C++ 计算移动平均,速度快很多
# 只取最后100个价格计算周期为20的SMA
sma_values = fast_sma(self.prices[-100:], 20)

# 获取计算出的最新的SMA值
sma = sma_values[-1]

# 用Python写交易逻辑,清晰易懂
if price > sma:
return "BUY"
elif price < sma:
return "SELL"

return "HOLD"

if __name__ == "__main__":
strategy = HybridStrategy()
# 模拟接收行情数据
for price in np.random.rand(200) * 100:
action = strategy.on_tick(price)
if action != "HOLD":
print(f"Price: {price:.2f}, Action: {action}")