You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用python-cffi包装C函数以支持Python关键字参数调用?

问题解答

CFFI 本身没有内置支持关键字参数调用封装后C函数的功能,默认导出的C函数绑定采用纯位置参数实现,目的是最大化调用性能,没有额外的关键字参数适配逻辑。

如果你不想逐个手写C函数的外层包装,可以通过通用批量包装的方式实现需求,只需要写一次通用逻辑即可适配所有导出的C函数,无需重复编码:

  1. 第一步先修改 example_extension_build.py 中的cdef声明,补全函数的参数名,否则无法建立参数名和位置的映射关系:
ffibuilder.cdef("""
    struct my_s{ int a; char * b; };
    int my_f(int arg_1, struct my_s arg_2); // 补上参数名
""")

重新编译扩展之后,就可以通过CFFI的类型接口拿到每个函数的参数名信息。

  1. 第二步在Python导入扩展时加入一次性的通用包装逻辑:
# file: test_example.py
import _example as e
from _example import lib, ffi

def wrap_c_func(c_func):
    func_type = ffi.typeof(c_func)
    arg_names = [p.name for p in func_type.args]
    def wrapper(*args, **kwargs):
        if not kwargs:
            return c_func(*args)
        call_args = list(args)
        for idx in range(len(args), len(arg_names)):
            arg_name = arg_names[idx]
            if arg_name not in kwargs:
                raise TypeError(f"{c_func.__name__} missing required argument: {arg_name}")
            call_args.append(kwargs.pop(arg_name))
        if kwargs:
            raise TypeError(f"{c_func.__name__} got unexpected keyword arguments: {list(kwargs.keys())}")
        return c_func(*call_args)
    wrapper.__name__ = c_func.__name__
    return wrapper

# 批量包装所有导出的C函数,不需要逐个处理
for attr_name in dir(lib):
    attr = getattr(lib, attr_name)
    if callable(attr):
        try:
            if hasattr(ffi.typeof(attr), 'args'):
                setattr(lib, attr_name, wrap_c_func(attr))
        except:
            pass

# 现在已经可以用关键字参数调用了
n = 21
s = e.ffi.new("struct my_s *")
s.a = 21
s.b = e.ffi.new("char[]", b"Hello World!")

lib.my_f(arg_2=s[0], arg_1=n) # 可以正常运行

这种方案的额外性能开销极低,也不需要你针对每个C函数单独编写包装代码,完全满足优雅实现的需求。

内容的提问来源于stack exchange,提问作者dornhe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.21 03:57:04