Python3调用C程序(超详解)

Python为什么要调用C?

1.要提高代码的运算速度,C比Python快50倍以上

2.对于C语言里很多传统类库,不想用Python重写,想对从内存到文件接口这样的底层资源进行访问

Python调用C的方法:

Python调用C的方法通常有3种:

1.SWIG,编写一个额外的接口文件来作为SWIG(终端工具)的入口

2.通过CTypes调用

3.使用Python/C API方法

第一种方法大多数情况下会带来不必要的麻烦,我并没有试验,本文只针对2,3方法作详细说明

通过CTypes调用:

Python中的ctypes模块可能是Python调用C方法中最简单的一种。ctypes模块提供了和C语言兼容的数据类型和函数来加载dll文件,因此在调用时不需对源文件做任何的修改。也正是如此奠定了这种方法的简单性。

下面是python文件的代码:

from ctypes import *	#pip ctypes库,并导入库
test = CDLL("./test.dll") #调用当前目录下叫test的dll文件,dll文件是C生成的动态链接库
result =test.sum(5,10) #调用库里的函数sum,求和函数
print(result) #打印结果

接下来用C语言编写dll动态链接库,这里使用VS:



单击头文件,新建项:

添加源文件:

在头文件test.h中加入如下代码:

#pragma once

#ifdef BUILD_TEST
#define API_SYMBOL __declspec(dllexport)
#else
#define API_SYMBOL __declspec(dllimport)
#endif
//宏定义,导出或者导入// extern "C" API_SYMBOL int sum(int x, int y);
//导出函数//

在源文件test.cpp中加入如下代码:

#define BUILD_TEST  //使导出函数生效
#include "test.h"
#include "stdio.h"
#include "pch.h" int sum(int a, int b) {
return a + b;
}

注意,这里要点击64位,再点击生成(因为目前大部分电脑安装Python解释器是64位的,否则默认生成32位的动态库,会导致无法调用)这是一个很隐蔽的坑!!!

在生成的动态库路径下找到test.dll文件,并复制到python项目下

最后在python中运行代码,出现如下问题:

为了寻找问题,我将python文件中的代码替换如下,发现调用动态库是成功的,只是不能调用动态库里面的函数

from ctypes import *
from ctypes import * #pip ctypes库,并导入库
test = CDLL("./test.dll") #调用当前目录下叫test的dll文件,dll文件是C生成的动态链接库 print("加载成功") # result =test.sum(5,10) #调用库里的函数sum,求和函数
# print(result) #打印结果

以上流程我是参考B站一位UP主的具体教学,但还是行不通,于是我将test.cpp源文件中的代码更改如下:

#define BUILD_TEST
#include "test.h"
#include "stdio.h"
#include "pch.h" #define DLLEXPORT extern "C" __declspec(dllexport) //直接在源文件定义导出 DLLEXPORT int sum(int a, int b) {
return a + b;
}//两数相加

重复上述流程,生成dll文件,将文件放置于python项目中,然后调用,终于成功

from ctypes import *
from ctypes import * #pip ctypes库,并导入库
test = CDLL("./test.dll") #调用当前目录下叫test的dll文件,dll文件是C生成的动态链接库 print("加载成功") result =test.sum(5,10) #调用库里的函数sum,求和函数
print(result) #打印结果

使用Python/C API方法:

Python/C API可能是被最广泛使用的方法。它不仅简单,而且可以在C代码中操作你的Python对象。但要使用这种方法,需要用特定的方式来编写C代码,所以C代码不是原生的C(大伙要适应一下),这样才可以供python去调用。这里参照python进阶的说明博客园的一篇文章

python文件的代码如下:

import Test
x = 1
print(Test.add_one(x))

而这里的Test模块,则是需要我们自己用C语言写,C文件代码如下:

#include <Python.h>

int add_one(int a){
return a + 1;
}//这是C的原生函数,实现+1功能 static PyObject * py_add_one(PyObject *self, PyObject *args){
int num;
if (!PyArg_ParseTuple(args, "i", &num)) return NULL;
return PyLong_FromLong(add_one(num));
}//这一串代码要实现的功能如下,按照python规定的调用方式:
//1.定义一个新的静态函数,接收2个PyObject *参数,返回1个PyObject *值
//2.PyArg_ParseTuple方法将python输入的变量变成C的变量,即上述args→num
//3.紧接着调用C原生函数add_one,传入num
//4.最后将调用返回的C变量,转换为PyObject*或其子类,并通过PyLong_FromLong方法,返回python值 static PyMethodDef Methods[] = {
{"add_one", py_add_one, METH_VARARGS},
{NULL, NULL}
};//这串代码的目的是创建一个数组,来指明Python可以调用这个扩展函数:
//其中"add_one",代表编译后python调用时希望使用的函数名。而py_add_one,代表要调用当前C代码中的哪一个函数,即static PyObject * py_add_one。METH_VARARGS,代表函数的参数传递形式,主要包括位置参数和关键字参数两种
//最后如果希望添加新的函数,则在最后的{NULL, NULL}里按同样格式填写新的调用信息,比如加一些求和求乘积函数 static struct PyModuleDef cModule = {
PyModuleDef_HEAD_INIT,
"Test", /*模块名,即是生成模块后的名字,如numpy,opencv等等*/
"", /* 模块文档,可以为空NULL */
-1, /* 模块中每个解释器的状态大小,模块在全局变量中保持状态,则为-1 */
Methods//即上一步定义的Methods,说明了可调用的函数集
};//创建module的信息 PyMODINIT_FUNC PyInit_Test(void){ return PyModule_Create(&cModule);}
//module初始化

将C文件放置在python项目的同文件目录下,然后编写setup.py文件,setup.py是用来将C打包成模块的脚本文件,代码如下:

from distutils.core import setup, Extension #这里要用到distutils库
module1 = Extension('Test', sources = ['test.c']) #打包文件为test.c,这里没有设置路径,所有.c文件和setup.py放在同一目录下
setup (name = 'Test', #打包名
version = '1.0', #版本
description = 'This is a demo package', #说明文字
ext_modules = [module1])

然后在pycharm的Terminal终端输入(因为这里用的python3.5+,windows平台下python的C/C++扩展不再支持gcc的编译,并强制要求使用msvc进行编译,如果版本低于3.5,用python setup.py build这是一个隐藏的坑!!!用python setup.py build一直出问题

python setup.py build --compiler msvc #编译代码
python setup.py build --compiler msvc install #编译代码并直接将包放入当前python环境的包的路径以供调用

最后结果,顺利打包,我们在python里安装了自己的包并完成调用:

本文由博客一文多发平台 OpenWrite 发布!

Python3调用C程序(超详解)的更多相关文章

  1. Linux Bash命令关于程序调试详解

    转载:http://os.51cto.com/art/201006/207230.htm 参考:<Linux shell 脚本攻略>Page22-23 Linux bash程序在程序员的使 ...

  2. UIViewController中各方法调用顺序及功能详解

    UIViewController中各方法调用顺序及功能详解 UIViewController中loadView, viewDidLoad, viewWillUnload, viewDidUnload, ...

  3. 一个简单的C语言程序(详解)

    C Primer Plus之一个简单的C语言程序(详解) #include <stdio.h> int main(void) //一个简单的 C程序 { int num; //定义一个名为 ...

  4. Python调用windows下DLL详解

    Python调用windows下DLL详解 - ctypes库的使用 2014年09月05日 16:05:44 阅读数:6942 在python中某些时候需要C做效率上的补充,在实际应用中,需要做部分 ...

  5. JUC中的AQS底层详细超详解

    摘要:当你使用java实现一个线程同步的对象时,一定会包含一个问题:你该如何保证多个线程访问该对象时,正确地进行阻塞等待,正确地被唤醒? 本文分享自华为云社区<JUC中的AQS底层详细超详解,剖 ...

  6. 嵌入式Linux应用程序开发详解------(创建守护进程)

    嵌入式Linux应用程序开发详解 华清远见 本文只是阅读文摘. 创建一个守护进程的步骤: 1.创建一个子进程,然后退出父进程: 2.在子进程中使用创建新会话---setsid(): 3.改变当前工作目 ...

  7. VS2010开发程序打包详解

    VS2010开发程序打包详解 转自:http://blog.sina.com.cn/s/blog_473b385101019ufr.html 首先打开已经完成的工程,如图: 下面开始制作安装程序包. ...

  8. Thrift实现C#调用Java开发步骤详解

    概述 Thrift实现C#调用Java开发步骤详解 详细 代码下载:http://www.demodashi.com/demo/10946.html Apache Thrift 是 Facebook ...

  9. html5的float属性超详解(display,position, float)(文本流)

    html5的float属性超详解(display,position, float)(文本流) 一.总结 1.文本流: 2.float和绝对定位都不占文本流的位置 3.普通流是默认定位方式,就是依次按照 ...

随机推荐

  1. Unity——射线系统

    Unity射线系统 Demo展示 UI+Physical射线测试: FPS自定义射线测试: UGUI射线工具 实现功能,鼠标点击UI,返回鼠标点击的UI对象: 需要使用到鼠标点击事件-PointerE ...

  2. CF #749

    A 题意 有个长度为n的序列, 每个数互不相同, 求总和最大的最长子序列, 并输出每个i: 题解 emmmmmm, 刚开始看到这个数据和题解被迷惑了, 以为有什么顺序, 并且一直在想一些复杂度较高的算 ...

  3. 几十行js实现很炫的canvas交互特效

    几十行js实现很炫的canvas交互特效 废话不多说,先上效果图! 本篇文章的示例代码都是抄的一个叫Franks的老外在yutube上的一个教学视频,他还出了很多关于canvas的视频,十分值得学习, ...

  4. Mac sourceTree每次都输入密码

    打开终端 依次输入以下三条命令 curl http://github-media-downloads.s3.amazonaws.com/osx/git-credential-osxkeychain - ...

  5. IP数据报中如果不分片,分片标志值是什么?

    过了好久才解决这个简单的问题,罪过罪过- 答案:如果IP数据报不分片,分片标志DF(Don't Fragment)会被设置为1.分片标志MF(More Fragment)设置为0. 下面是详细解释: ...

  6. 优客源创会 西安站 西邮Linux兴趣小组

    2016年5月19日晚7:00,优客源创会西安站在西安邮电大学长安校区东区教学楼FF305如期举行,西安邮电大学计算机学院教授.西邮Linux兴趣小组指导老师陈莉君.王小银老师和来自开源中国的周凯先生 ...

  7. SpringBoot热部署(7)

    1.引入热部署依赖包 <dependency> <groupId>org.springframework.boot</groupId> <artifactId ...

  8. 【java+selenium3】多窗口window切换及句柄handle获取(四)

    一 .页面准备 1.html <html> <head> <title>主页面 1</title> </head> <body> ...

  9. js事件常用操作、事件流

    注册事件 给元素添加事件,称为注册事件或者绑定事件. 注册事件有两种方式:传统方式和方法监听注册方式 传统方式 on开头的事件,例如onclick <button onclick="a ...

  10. yrm的安装和使用

    yrm的安装和使用 我们经常下载包的速度很忙有的还会卡住几十分钟,所以我们需要切换镜像,这样我们下载的速度会快很多 而yrm 是一个 yarn源管理器,允许你快速地在源间切换 安装 npm insta ...