关于.NET调用原生Python

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/f529324416/article/details/80142318

    会研究这个课题纯粹是因为尝试一下能否实现。其实关于.NET调用Python,已经有了IronPython,但是IronPython的更新缓慢,库安装也不如原生Python那么方便。(大佬可以无视我),如果能实现.NET调用Python,那么两者混合编程开发将会提高效率,在某些特殊的应用场景中。

    其实混合编程方案众多,例如开发本地服务器,或者利用文件来进行交互。但是一来速度慢,二来不够安全。虽然很多会这样实现吧。我认为Python也提供了更加安全的方式。

    原理大家都懂,Python是C开发的,所以通过C调用Python,然后在用托管C++开发CLR类库给C#调用。这样,我们只需要对Python源码和CLI有一定的认识和了解就可以做到了。并不是什么难事,以下是我个人的开发过程

   首先我们尝试使用C++来调用Python,网上教程很多。我新建了一个C++空项目。

需要注意的是,我们调整平台为我们当前Python版本的平台,比如32位Python就用x86,然后我们设置工程的属性,添加Python库到我们的工程

其中包含目录为Python安装文件夹下的include文件夹,库目录和引用目录都设置为Python安装文件夹下的libs文件夹。确定后就可以开始编写代码了。

首先还是引用两个基本头文件和一个Python头文件,Python中所有的功能都会集中到Python.h当中。我们可以适当浏览一下Python.h中的内容

尝试调用Python的方法是循序渐进的,我们先检测是否可以初始化Python解释器。关于Python.h中的函数,我不做一一赘述了。只做简单的注释,大家可以自己查阅。

然后Python解释器提供了判断是否成功加载Python解释器的方法。Py_IsInitialized(),有个很有意思的点就是Python解释器只能初始化一个,(这也是为什么我们需要virtualenv构建虚拟环境来开发Python工程)与之相对的,大家很喜欢把它和Lua作比较,因为Lua的解释器可以初始化多个,每个解释器都可以执行不同的任务,相当于多进程,但是进程间也可以通信。

#include <iostream>
#include <stdio.h>
#include <Python.h>			//引用Python头文件,quote Python.h



int main(int* argc, char argv[]) {
	//入口点,entry point

	Py_Initialize();			//加载Python解释器到内存中,Load Python virtual mechine to memory
	if (Py_IsInitialized()) {
		std::cout << "成功加载Python解释器" << std::endl;
	}
	else {
		std::cout << "未成功加载Python解释器" << std::endl;
	}

	Py_Finalize();				//从内存中卸载Python解释器,Unload Python virtual mechine from memory
        getchar();
        return 1;
 }

如期获得以上的结果,然后我们尝试执行简单的代码。

添加了两行代码,代码主要是用于打开一次百度网页。执行后的结果也是如期打开百度网页,这里就不展示了。

接下来,我们在尝试用C++调用Python脚本中的一个简单的函数,函数用于输出一个helloworld,我们在目标exe同文件夹下放置一个python脚本。

编写相对应的函数如下

随后,我们再开始编写C++代码如下

#include <iostream>
#include <stdio.h>
#include <Python.h>			//引用Python头文件,quote Python.h



int main(int* argc, char argv[]) {
	//入口点,entry point

	Py_Initialize();			//加载Python解释器到内存中,Load Python virtual mechine to memory
	if (Py_IsInitialized()) {
		std::cout << "成功加载Python解释器" << std::endl;

		PyObject* PyMod = PyImport_ImportModule("hello");
		//导入hello模块,类似于python中的import
		//import hello module,just like 'import module' in Python code
		if (PyMod) {
			//检测是否成功的导入了模块,check if PyMod is valid

			PyObject* PyFunc = PyObject_GetAttrString(PyMod, "hello");

			//从PyMod中获得hello函数,这是一个Python中的深度魔法
			//Obtain hello function from PyMod,and this is a deep magic in Python

			if (PyFunc) {
				//检测是否成功的引入了函数,check if PyFunc is valid

				PyObject_CallObject(PyFunc, NULL);

				//执行函数,call function
			}
			else {
				std::cout << "没有成功的引入Python函数" << std::endl;
			}
		}
		else {
			std::cout << "没有成功的引入Python模块" << std::endl;
		}

		
	}
	else {
		std::cout << "未成功加载Python解释器" << std::endl;
	}

	Py_Finalize();				//从内存中卸载Python解释器,Unload Python virtual mechine from memory
	getchar();
	return 1;
}

最后执行函数没有任何问题,

理解了这些以后,我们可以把我们写的C++代码再封装成CLR类库

以上是我封装的基本的函数,其中DNetPython中主要是Python原生的函数,没有做任何改动,MCPython中是由于CLI的诸多问题而没有办法实现的Python调用,被我稍微调整了一下,以适应.NET的调用。

这个源代码和已经可以在csdn下载了,我放个链接

https://download.csdn.net/download/f529324416/10382724

接下来我们就尝试用C#来调用一下,先新建一个控制台引用程序,然后引入我们刚才编译好的托管dll

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            DNetPython.NetPython.DNet_Py_Initialize();  //load python virtual mechine
            IntPtr PyMod =  DNetPython.NetPython.DNet_PyImport_ImportModule("hello");
            IntPtr PyFunc = DNetPython.NetPython.DNet_PyObject_GetAttrString(PyMod, "hello");
            DNetPython.NetPython.DNet_PyObject_CallObject(PyFunc, new IntPtr(0));

            DNetPython.NetPython.DNet_Py_Finalize();    //unload python virtual mechine
            Console.ReadLine();
        }
    }
}

执行的结果也是正确的,但是要注意几点,这个ImportModule 这个函数导入的模块必须是在同文件夹下的py脚本,而且Net的运行平台必须是x64或者x86,不能是AnyCPU,(这也暗示着我们调用的其实也不算是原生Python,而是原生Python的Windows版本,如果需要真正的,可以添加到Mono中的Python脚本,我们需要在Linux平台编译合适的版本)

好了,如果有任何问题,欢迎加我qq529324416,或者加入我的群493486382讨论。

展开阅读全文

没有更多推荐了,返回首页