Python3调用C程序(超详解)
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程序(超详解)的更多相关文章
- Linux Bash命令关于程序调试详解
转载:http://os.51cto.com/art/201006/207230.htm 参考:<Linux shell 脚本攻略>Page22-23 Linux bash程序在程序员的使 ...
- UIViewController中各方法调用顺序及功能详解
UIViewController中各方法调用顺序及功能详解 UIViewController中loadView, viewDidLoad, viewWillUnload, viewDidUnload, ...
- 一个简单的C语言程序(详解)
C Primer Plus之一个简单的C语言程序(详解) #include <stdio.h> int main(void) //一个简单的 C程序 { int num; //定义一个名为 ...
- Python调用windows下DLL详解
Python调用windows下DLL详解 - ctypes库的使用 2014年09月05日 16:05:44 阅读数:6942 在python中某些时候需要C做效率上的补充,在实际应用中,需要做部分 ...
- JUC中的AQS底层详细超详解
摘要:当你使用java实现一个线程同步的对象时,一定会包含一个问题:你该如何保证多个线程访问该对象时,正确地进行阻塞等待,正确地被唤醒? 本文分享自华为云社区<JUC中的AQS底层详细超详解,剖 ...
- 嵌入式Linux应用程序开发详解------(创建守护进程)
嵌入式Linux应用程序开发详解 华清远见 本文只是阅读文摘. 创建一个守护进程的步骤: 1.创建一个子进程,然后退出父进程: 2.在子进程中使用创建新会话---setsid(): 3.改变当前工作目 ...
- VS2010开发程序打包详解
VS2010开发程序打包详解 转自:http://blog.sina.com.cn/s/blog_473b385101019ufr.html 首先打开已经完成的工程,如图: 下面开始制作安装程序包. ...
- Thrift实现C#调用Java开发步骤详解
概述 Thrift实现C#调用Java开发步骤详解 详细 代码下载:http://www.demodashi.com/demo/10946.html Apache Thrift 是 Facebook ...
- html5的float属性超详解(display,position, float)(文本流)
html5的float属性超详解(display,position, float)(文本流) 一.总结 1.文本流: 2.float和绝对定位都不占文本流的位置 3.普通流是默认定位方式,就是依次按照 ...
随机推荐
- 极速上手 VUE 3—v-model 的使用变化
本篇文章主要介绍 v-model 在 Vue2 和 Vue3 中使用变化. 一.Vue2 中 v-model 的使用 v-model 是语法糖,本质还是父子组件间的通信.父子组件通信时有两种方式: 父 ...
- 如何抓取直播源及视频URL地址-疯狂URL(教程)
直播源介绍 首先,我们来快速了解一下什么是直播源,所谓的直播源,其实就说推流地址,推流地址可能你也不知道是什么,那么我再简单说一下,推流地址就是,当某个直播开播的时候,需要将自己的直播状态实时的展示给 ...
- Mysql 5.7 集群部署,keepalived
参考文章: https://blog.csdn.net/f18770366447/article/details/80703347 https://www.cnblogs.com/benjamin77 ...
- 关于Arrays类的静态方法asList()
Array.asList():是数组转成集合的方法 List<String> list = Arrays.asList(new String[]{"AA", " ...
- 【Python+postman接口自动化测试】(5)抓包工具Fiddler简介
Fiddler简介 Fiddler 4.6 下载 http://www.downza.cn/soft/234727.html 为什么使用Fiddler? 可以抓到请求数据,查看Raw格式/表单格式/J ...
- Jmeter 正则表达式提取Response Headers,Response Body里的值
实践过程中遇到需要提取Response Headers,Response Body里的值 一.获取Response Body的值,这里采用json提取器形式 1.Response Body返回值,如下 ...
- 解决IntelliJ IDEA的Plugins无法访问Marketplace去下载插件
本文图文讲解如何解决IntelliJ IDEA的Plugins无法访问Marketplace去下载插件. 默认打开IDEA的Plugins会加载很久,最后什么也没加载出来. 这时我们可以给插件市场设置 ...
- 浅谈web前端优化
开篇 优化网站是一个系统性和持续性的过程.很多人认为优化网站的性能只需要合并图片啦,减小HTTP请求啦,部署CDN啦就行,实际上这都是见木不见林的做法.以上的做法经常会被面试者提起,在被问到自己在网页 ...
- Go defer 特性和使用场景
golang 的 defer 语句用于延迟调用.defer 会在当前函数返回之前执行 defer 注册的函数.比如 defer func_defer() 这样语句会让你注册一个函数变量到 defer ...
- Spark性能调优——9项基本原则
原则一:避免创建重复的RDD 通常来说,我们在开发一个Spark作业时,首先是基于某个数据源(比如Hive表或HDFS文件)创建一个初始的RDD:接着对这个RDD执行某个算子操作,然后得到下一个RDD ...