该考虑写c扩展当且仅当python逻辑已成性能瓶颈、numba不支持热点函数、或ctypes/cffi因频繁内存拷贝拖慢性能;否则优先选pybind11或cffi。

什么时候该考虑写 C 扩展,而不是用 Cython 或 numba
Python 调用纯 Python 逻辑慢到瓶颈、且热点函数无法被 numba.jit 支持(比如涉及动态类型、字典嵌套、异常控制流),或者你已经在用 ctypes / cffi 但频繁内存拷贝拖垮性能——这时候才值得上 C 扩展。别为“听说 C 快”就动手,90% 的场景 pybind11 或 cffi 更省事。
常见错误现象:ImportError: dynamic module does not define module export function,往往是因为 PyModuleDef 结构体没填对,或模块初始化函数名不匹配。
- 使用场景:高频调用的数值计算内核、自定义序列化/解析器、绕过 GIL 的 I/O 密集型操作
- 参数差异:
PyObject *是唯一入口参数类型,所有 Python 对象都得从它 unpack;C 层不能直接用int接float类型的 Python 参数 - 性能影响:如果 C 函数里频繁调用
PyLong_AsLong或PyList_GetItem,反而比纯 Python 慢——C 扩展快的前提是“少跨边界、少查类型、少分配 Python 对象”
如何让 C 扩展不崩溃 Python 解释器
核心原则:只要 C 层抛了未捕获的 SIGSEGV、访问了已释放的 PyObject *、或在 GIL 释放后误用 Python C API,解释器就直接 abort。这不是报错,是段错误。
实操建议:
立即学习“Python免费学习笔记(深入)”;
创想C2C商城系统,系统功能仿照淘宝设计,采用模块标签技术和静态html生成技术 基于Asp.Net/C#+SQL的开发的创想多用户商城系统,具有智能化、高扩展、稳定安全等特性,后台可自由添加频道,自由修改界面风格,商品无限级 分类,支持在线支付整合,通过安装和使用创想C2C商城系统,就可以轻松建立起专业大型的网上交易平台。创想C2C多用户商城系统5.6.3.8版本升级功能1.网站地区设置功能的增
- 所有
PyArg_ParseTuple后必须检查返回值,失败时立即return NULL,不能继续执行 - 用
Py_INCREF/Py_DECREF管理引用计数——尤其在循环中反复赋值item = PyList_GetItem(...)时,item不增加引用,不能直接存起来后续用 - 若需释放 GIL(如长耗时计算),必须用
Py_BEGIN_ALLOW_THREADS/Py_END_ALLOW_THREADS包裹,并确保中间不调任何 Python C API - 调试时加
-g -O0编译,用gdb python -c "import yourmod"捕获 core dump
setup.py 编译 C 扩展最简可靠写法
别碰 distutils,用 setuptools.Extension + setup(),且显式指定 include_dirs 和 extra_compile_args。Windows 上尤其容易因 MSVC 版本不匹配导致 LNK2001。
关键点:
-
Extension的sources列表必须包含 .c 文件全路径,不能只写文件名 - Linux/macOS 加
extra_compile_args=['-fPIC'],否则ImportError: dynamic module does not define module export function - 避免用
python setup.py build_ext --inplace反复编译——.so/.pyd 文件可能被占用,改完 C 代码后先删掉build/目录再重来 - 验证是否成功:
python -c "import yourmod; print(yourmod.__file__)",输出路径应指向 .so/.pyd,不是 .py
为什么你的 C 扩展跑得还没纯 Python 快
典型原因不是 C 写得差,而是边界开销吃掉了全部收益。一次 PyObject * 到 double 的转换成本 ≈ 50 条 C 浮点指令;传 1000 个 int 进去,逐个 PyLong_AsLong,不如 Python 层用 array.array 或 numpy.ndarray 一次性传内存块。
- 正确做法:用
PyMemoryView_FromObject或PyArray_FROM_OTF(配 numpy)接数组,直接操作 data 指针 - 避免在 C 层构造新
PyList或PyDict返回——能用Py_BuildValue("i", result)就别手动生成对象 - Windows 上注意
__declspec(dllexport)不要漏写,否则ImportError报得毫无提示 - 真正快的 C 扩展,90% 代码在做数据搬运和校验,核心计算逻辑往往就十几行
最难的从来不是写 C,是想清楚哪部分真该交给 C —— 多数人卡在把 Python 的抽象泄漏当成性能瓶颈。










