我妒忌你的开源,你眼红我的速度,不如我们就在一起吧! --------SJ2050

2019.4.9号更新:实现在未安装python环境的机子上运行调用了python程序的C语言代码!


环境搭建

这篇教程基于的实验环境为VS2017+python3.5.0,所以我们首先来进行一些前期准备使得小C能认识小派(做个媒婆),说白了就是VS中第三方库的配置过程,我在这篇文章中已有详细图文介绍?两分钟搞定VS下第三方库的配置?。这里我们只强调一下不同点:


代码讲解

虽然网上已有不少的C语言调用python代码的实例了,但大多是抄来抄去,比较难以理解,而且前期铺垫了很多,代码部分就被带过了,让读者很苦恼,这也是我写这篇文章的缘由。我们首先来看一下我们将要调用的python的代码,很简单的一个函数:

# -*- coding: utf-8 -*-
# this function will return "I Love you,***" def express_love(name):
return 'I Love you,%s!'%name,

我相信,既然你在找到了这篇文章,你一定是对C和python有一定的了解,上面的python函数不过是传入一个参数,然后打印出一句肉麻的话来。接下来我们着重看一下C的代码:

#include <stdio.h>
#include <stdlib.h>
#include <Python.h> int main()
{
// variable declarations of PyObject type
PyObject *pModule, *pFunc;
PyObject *pArgs, *pValue;
char* saying; // initialize the running environment
Py_Initialize(); //import python scripts
pModule = PyImport_ImportModule("iloveyou"); // error checking of pModule left out
if (pModule){
// get the specific function in python scripts
pFunc = PyObject_GetAttrString(pModule, "express_love"); if (pFunc && PyCallable_Check(pFunc)) {
// create a tuple variable of python
pArgs = PyTuple_New(1);
PyTuple_SetItem(pArgs, 0, Py_BuildValue("s","小 C")); if (pArgs) {
pValue = PyObject_CallObject(pFunc,pArgs); if (!pValue) {
// pValue error
PyErr_Print();
return -1;
} // convert python variables to C variables
if (PyArg_ParseTuple(pValue, "s", &saying)) {
printf("小派对小C说:%s\n", saying);
Py_XDECREF(pValue);
}
else {
// convertion fails
PyErr_Print();
return -1;
}
Py_XDECREF(pArgs);
}
else {
// pArgs error
PyErr_Print();
return -1;
}
Py_XDECREF(pFunc);
}
else {
// pFunc error
PyErr_Print();
return -1;
}
Py_XDECREF(pModule);
}
else {
// pModule error
PyErr_Print();
return -1;
} // end the environment
Py_Finalize(); system("pause");
return 0;
}

C的代码乍一眼看起来很长,很吓人的样子,只不过是注释比较多,还有很多错误检测的代码罢了,实际工作的代码少的可怜,接下来我们逐部分分析这段代码:

  1. 标准的C语言的框架,唯一要提醒的是这里我们要导入Python的头文件,这样我们才能使用python给我们准备的一些函数和数据结构呀!
#include <stdio.h>
#include <stdlib.h>
#include <Python.h> int main()
{
//....
system("pause");
return 0;
}
  1. PyObject是个结构体,是所有python变量的基类,可以简单当作个智能类型,其内部会自动转换。这里声明的变量都使用了指针类型,无指针不C呀!
// variable declarations of PyObject type
PyObject *pModule, *pFunc;
PyObject *pArgs, *pValue;
char* saying
  1. 因为要调用python代码,所以得初始化运行环境(原生态的小C不认识小派呀)。
// initialize the running environment
Py_Initialize();
  1. PyImport_ImportModule()函数是导入我们要调用的python代码,其传入参数是python文件名,然后我们 把该文件名的python代码放在和可执行文件(.exe)同一个文件夹下
//import python scripts
pModule = PyImport_ImportModule("iloveyou");
  1. 这个选择语句检查导入python文件是否成功,若不成功,其会返回NULL,即执行else中的语句,PyErr_Print()函数会把错误信息打印出来。
// error checking of pModule left out
if (pModule){
//...
}
else {
// pModule error
PyErr_Print();
return -1;
}
  1. PyObject_GetAttrString()函数可以获得导入python模块的指定属性和方法,这里我们导入python代码中的函数express_love。
	// get the specific function in python scripts
pFunc = PyObject_GetAttrString(pModule, "express_love");
  1. 检查导入python属性是否成功,且能否调用(即是否为函数),else部分中的语句之前已提到过。
		if (pFunc && PyCallable_Check(pFunc)) {
//...
}
else {
// pFunc error
PyErr_Print();
return -1;
}
  1. PyTuple_New()函数可以创建一个python中的元组类型,传入的参数可以指定这个元组的大小。接下来的PyTuple_SetItem()函数可以为元组赋值,其第一个参数是元组类型的变量,第二个参数是要赋值的下标,第三个参数是python的变量,可以是任何python的变量类型。所以,我们需要Py_BuildVaule()这个函数可以创建python变量,这个函数的用法很类似C中的printf,第一个参数是格式化参数,第二个是C字符串,这里的s表示我们要创建一个python中的字符串。
    pArgs = PyTuple_New(1);
PyTuple_SetItem(pArgs, 0, Py_BuildValue("s","小 C"));
  1. 检查创建python中的变量是否成功。<./font>
				if (pArgs) {
//...
}
else {
// convertion fails
PyErr_Print();
return -1;
}
  1. PyObject_CallObject()是调用python代码中的函数,第二个参数是传给函数的参数,注意,这个参数一定得为python的元组类型,这个参数一定得为python的元组类型,这个参数一定得为python的元组类型。执行后,这个函数会返回指向return结果的PyObject指针。
pValue = PyObject_CallObject(pFunc,pArgs);
  1. 检查调用python代码中的函数是否出错。
	if (!pValue) {
// pValue error
PyErr_Print();
return -1;
}
  1. PyArg_ParseTuple()函数会将python变量转化为C变量,用法和fprintf()比较类似,如果转化不成功,这个函数会返回NULL,这里我们也得注意,其第一个参数得为python的元组类型,第一个参数得为python的元组类型,第一个参数得为python的元组类型。
   			 // convert python variables to C variables
if (PyArg_ParseTuple(pValue, "s", &saying)) {
printf("小派对小C说:%s\n", saying);
}
else {
// convertion fails
PyErr_Print();
return -1;
}
  1. 最后我们结束并清理python的运行环境。
	// end the environment
Py_Finalize();

这样子,我相信你对这段代码应该是没有疑惑了。


引用计数

细心的小朋友可能会发现,在讲解代码过程中,有个函数一直被漏掉了——Py_XDECREF()。这个是干嘛的呢,这还得从python的引用计数的机制说起。众所周知,python很智能,即使我们打开文件后未显示调用close,但python的解释器也会适时去关闭它,这若是搁C这,就引发内存泄露了。那么python是怎么知道哪些变量是可以销毁,哪些不是呢?这就用到了引用计数机制,python对象中记录了其被引用了多少次,当引用次数为0时,也就是其不备任何python变量引用,则python解释器就会将其销毁。PyObject结构体中的有一个变量就记录了引用的次数(好期的朋友可以在VS中查看其定义)。一般情况下,引用的增加和减少在我们调用函数过程中会自动完成,但有时候我们也可以显性地来增加和减少引用,例如Py_XINCREF(),Py_XDECREF()(有人问为啥不用Py_INCREF()和Py_DECREF(),因为加个X可以不管参数是否为NULL)。

       如何正确且合理地应用好引用计数是项高难度地技术活,这里我们不详细展开了,我找了一篇我认为比较好的相关文章?Python基础30-面向对象(内存管理机制-引用计数/垃圾回收/循环引用/弱引用)?。


特别说明

上面这段代码在VS运行中会在return部分报错,请教了几位大神,尚不清楚问题所在,如果你知道的话,麻烦告诉我一下。但我们直接去运行编译生成的可执行的文件(.exe)的话,倒是一切正常。我将这段代码在codeblocks编译后运行也一切正常,VS太强大,我也搞不清楚他在想啥。

       **如果想让可执行文件运行在别人的电脑上装X的话,那么务必请确保别人的电脑也安装了python,且版本和你电脑上的python相同,不然的话就会报找不到相应dll的错误。最后,我们来看一下最终这个可执行文件运行的效果: **



       如果想在不安装python环境的机子上运行该程序,就得将python35.dll(根据版本选择)以及整个Lib库拷贝到与exe同级目录下(整个Lib文件夹显得有点大,如果你知道这个程序中具体调用了哪些库,完全可以复制那几个库,但我强烈建议不要自找麻烦),上张图便能说明一切:

参考资料

1、《python3.5.0帮助文档》

2、《python高级编程(第二版)》

小C和小派的缠绵爱情——C语言调用Python代码的更多相关文章

  1. APP跳转小程序,小程序跳转APP

    关注公共号,搜索 "APP跳转小程序,小程序跳转APP",查看原文 前置条件: 开发环境:windows 开发框架:uni-app , H5+,nativeJS,mpvue 编辑器 ...

  2. hdu4505小Q系列故事——电梯里的爱情

    小Q系列故事——电梯里的爱情 Time Limit: 300/100 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)Tota ...

  3. 小Q系列故事——电梯里的爱情

    小Q系列故事——电梯里的爱情 Time Limit : 300/100ms (Java/Other)   Memory Limit : 65535/32768K (Java/Other) Total ...

  4. 有趣 GIF 动图集 - 仿佛每张小动图都诉说了一个小笑话或者小故事

    点这里 来自法国南特(Nantes)的 Guillaume Kurkdjian 目前还是个学生.Kurkdjian 擅长创作一些平面动态图像,这些有趣的小动图仿佛每张都诉说了一个小笑话或者小故事,像个 ...

  5. 微信小程序——智能小秘“遥知之”源码分享(语义理解基于olami)

    微信小程序智能生活小秘书开发详解 >>>>>>>>>>>>>>>>>>>>> ...

  6. 微信小程序+“芝麻小客服”可设自动关注公众号,助力运营闭环

    微信小程序全面上线已经接近1年的时间,从最初的"用完即走"理念到2017年总计更新开放60余次的功能创新,微信小程序不一定会爆发下一次的红利,但绝对是微信生态中重要的一环. 芝麻小 ...

  7. 小白突破百度翻译反爬机制,33行Python代码实现汉译英小工具!

    表弟17岁就没读书了,在我家呆了差不多一年吧. 呆的前几个月,每天上网打游戏,我又不好怎么在言语上管教他,就琢磨着看他要不要跟我学习Python编程.他开始问我Python编程什么?我打开了我给学生上 ...

  8. 玩玩小程序:使用 WebApi 交互打造原生的微信小程序 - 图灵小书架

    使用 WebApi 交互打造原生的微信小程序 - 图灵小书架 目录 介绍 源码地址 扫一扫体验 代码分析 其它相关信息(互联网搜集) 介绍 定时抓取图灵社区官网的首页.最热.推荐和最新等栏目的相关图书 ...

  9. 用css 添加手状样式,鼠标移上去变小手,变小手

    用css 添加手状样式,鼠标移上去变小手,变小手 cursor:pointer; 用JS使鼠标变小手onmouseover(鼠标越过的时候) onmouseover="this.style. ...

随机推荐

  1. python温度转换代码分析

    将用户输入的温度信息保存在TempStr变量中 if分支条件,判断TempStr类型是否在f及F列表之中 如果用户输入的在f及F列表之中,则用户输入的是一个华氏温度值,对华氏温度进行摄氏温度的转换,e ...

  2. TKE 集群组建最佳实践

    Kubernetes 版本 Kubernetes 版本迭代比较快,新版本通常包含许多 bug 修复和新功能,旧版本逐渐淘汰,建议创建集群时选择当前 TKE 支持的最新版本,后续出新版本后也是可以支持 ...

  3. 如何使用 C# 中的 ValueTask

    在 C# 中利用 ValueTask 避免从异步方法返回 Task 对象时分配 翻译自 Joydip Kanjilal 2020年7月6日 的文章 <How to use ValueTask i ...

  4. PHP:文件包含漏洞

    简单记录一些文件包含漏洞的常用方法 产生原因: 文件包含漏洞的产生原因是在通过引入文件时,由于传入的文件名没有经过合理的校验,或者校检被绕过,从而操作了预想之外的文件,就可能导致意外的文件泄露甚至恶意 ...

  5. Python爬取股票信息,并实现可视化数据

    前言 截止2019年年底我国股票投资者数量为15975.24万户, 如此多的股民热衷于炒股,首先抛开炒股技术不说, 那么多股票数据是不是非常难找, 找到之后是不是看着密密麻麻的数据是不是头都大了? 今 ...

  6. ByPass Mode(略过模式或旁路模式)

    参考: 1. https://baike.baidu.com/item/%E6%97%81%E8%B7%AF%E6%A8%A1%E5%BC%8F/3120563 2. https://zhidao.b ...

  7. 14.深入k8s:kube-proxy ipvs及其源码分析

    转载请声明出处哦~,本篇文章发布于luozhiyun的博客:https://www.luozhiyun.com 源码版本是1.19 这一篇是讲service,但是基础使用以及基本概念由于官方实在是写的 ...

  8. apt-get 安装软件时出现:“文件尺寸不符” 问题

    报错信息 命中:1 http://packages.deepin.com/deepin panda InRelease 命中:2 http://linux.teamviewer.com/deb sta ...

  9. HTML CSS+JS想要做放大镜练习,如何获取同样的大图和小图?

    1.进入某商城找到对应的图片: 步骤一: 步骤二: 步骤三: 2.检查源代码: 情况一:按F12 情况二:鼠标在网页内,直接右键-->"检查元素" 1.选中选择部分 2.点击 ...

  10. (OK) Android内核(4.9)集成最新版MPTCP---成功

    Android内核(4.9)集成最新版MPTCP---成功