C#/.net程序调用python

C#的优势在于window下的开发,不仅功能强大而且开发周期短。而python则有众多的第三方库,可以避免自己造轮子,利用C#来做界面,而具体实现使用python来实现可以大大提高开发效率。本文介绍如何使用pythonnet来执行python脚本,使用pythonnet既可以具有较高的交互性,又可以使用第三方python库,同时可以将程序需要的python环境及第三方库打包到软件中,避免用户进行python的环境配置。

C#调用python的常见方法

调用python常见的方法有4种

方式 优点 缺点
使用IronPython 无需安装python运行环境,交互性强,C#和python无缝连接 某些python第三方库不支持,如numpy
使用C++调用Python,然后将C++程序做成动态链接库 交互性较强 需要用户配置Python环境,实现方式复杂
利用C#命令行调用py文件 执行速度快 需要用户配置Python环境,交互性差
将python文件打包成exe进行调用 无需安装python运行环境, 执行速度慢,传递数据复杂,交互性差

可以看出4种方式均有限制,很难同时满足交互性强、可调用第三方python库、无需用户配置Python环境要求,而这几项要求恰恰是一款成熟软件所必须的。而使用pythonnet库可满足以上三点要求。

本文均在.net 6环境下测试

使用pythonnet

  1. Nuget安装pythonnet

  2. 设置Runtime.PythonDLL属性,即pythonxx.dll路径,xx为版本号

  3. 设置PythonEngine.PythonHome,即python.exe所在路径

  4. 设置PythonEngine.PythonPath,python脚本所在目录,可以放置多个路径,以分号隔开,但是pathToVirtualEnv\Lib\site-packages和pathToVirtualEnv\Lib应放在最后

  5. 调用PythonEngine.Initialize();

    string pathToVirtualEnv = ".\\envs\\pythonnetTest";
    Runtime.PythonDLL = Path.Combine(pathToVirtualEnv, "python39.dll");
    PythonEngine.PythonHome = Path.Combine(pathToVirtualEnv, "python.exe");
    PythonEngine.PythonPath = $"{pathToVirtualEnv}\\Lib\\site-packages;{pathToVirtualEnv}\\Lib";
    PythonEngine.Initialize();
    //调用无参无返回值方法
    using (Py.GIL()) //执行python的调用应该放在using (Py.GIL())块内
    {
    //python对象应声明为dynamic类型
    dynamic np = Py.Import("test");
    np.hello();
    }
    //调用有参有返回值方法
    using (Py.GIL())
    {
    dynamic np = Py.Import("test");
    int r = np.add(1, 2);
    Console.WriteLine($"计算结果{r}");
    }

python文件,必须放在PythonEngine.PythonPath设定的目录下

def hello():
print("hello") def add(a,b):
return a+b

嵌入Python环境及使用第三方库

程序中包含Python脚本所需要的所有环境以及第三方库可以免去用户的自定义配置。本文使用Anaconda来构建专用的虚拟环境。

  1. 创建专用虚拟环境(windows下首先切换到要建立虚拟环境的根目录下),执行conda create --prefix=F:\condaenv\env_name python=3.7 路径及python版本根据需要自定义。

  2. 使用Anaconda Prompt,激活虚拟环境conda activate F:\condaenv\env_name

  3. 本次测试第三方库Numpy(如果需要其他库,安装方法相同),安装Numpypip install numpy

    string pathToVirtualEnv = ".\\envs\\pythonnetTest";
    Runtime.PythonDLL = Path.Combine(pathToVirtualEnv, "python39.dll");
    PythonEngine.PythonHome = Path.Combine(pathToVirtualEnv, "python.exe");
    PythonEngine.PythonPath = $"{pathToVirtualEnv}\\Lib\\site-packages;{pathToVirtualEnv}\\Lib";
    PythonEngine.Initialize()
    //使用第三方库
    using (Py.GIL())
    {
    dynamic np = Py.Import("numpy");
    Console.WriteLine(np.cos(np.pi * 2)); dynamic sin = np.sin;
    Console.WriteLine(sin(5)); double c = (double)(np.cos(5) + sin(5));
    Console.WriteLine(c); dynamic a = np.array(new List<float> { 1, 2, 3 });
    Console.WriteLine(a.dtype); dynamic b = np.array(new List<float> { 6, 5, 4 }, dtype: np.int32);
    Console.WriteLine(b.dtype); Console.WriteLine(a * b);
    Console.ReadKey();
    }

    注意:C#和python对象进行数学运算时,必须将Python对象放到前面,例如np.pi*2,不能是2*np.pi

传递对象

可以将C#对象传递到python中

在C#中定义对象

public class Person
{
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
} public string FirstName { get; set; }
public string LastName { get; set; }
}
string pathToVirtualEnv = ".\\envs\\pythonnetTest";
Runtime.PythonDLL = Path.Combine(pathToVirtualEnv, "python39.dll");
PythonEngine.PythonHome = Path.Combine(pathToVirtualEnv, "python.exe");
PythonEngine.PythonPath = $"{pathToVirtualEnv}\\Lib\\site-packages;{pathToVirtualEnv}\\Lib";
PythonEngine.Initialize();
//将C#中定义的类型传入python
using (Py.GIL())
{
Person p = new Person("John", "Smith");
PyObject pyPerson = p.ToPython();
string r1 = test.FullName(pyPerson);
Console.WriteLine($"全名:{r1}");
}

python脚本

def FullName(p):
return p.FirstName+""+p.LastName

调用pyd文件

pyd文件主要有以下2点作用:

  1. 安全性更高:通过pyd生成的文件,已变成了dll文件,无法查看源码
  2. 编译成pyd后,性能会有提升

将.py文件编译成pyd文件步骤如下:

  1. pip install cython
  2. 在.py文件目录下创建setup.py文件
from distutils.core import setup
from Cython.Build import cythonize setup(
name = "testName",
ext_modules = cythonize("test.py"), #将test.py文件编译成pyd
)
  1. 执行编译命令

python setup.py build_ext --inplace

最后生成的pyd文件一般是test+cpython版本-平台为文件名,可以重命名为test名称,也可以不管,使用时仍然可以按test调用。

调动pyd文件和调用py文件相同,但是执行效率大大增强,下文会对执行速度进行对比。

执行速度对比

在test.py中定义一个耗时函数

import time

def Count():
start = time.perf_counter() sum = 0
for i in range(10000):
for j in range(10000):
sum = sum + i + j
print("sum = ", sum) end = time.perf_counter()
runTime = end - start
runTime_ms = runTime * 1000 print("运行时间:", runTime, "秒")
  • 直接执行test.py脚本,运行结果如下:

  • 在C#中调用Conut()函数
//运行时间测试
Console.WriteLine("C#开始计时");
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
test.Count();
stopWatch.Stop();
Console.WriteLine($"C#计时结束{stopWatch.ElapsedMilliseconds}");

执行结果如下:

可以看到,使用pythonnet调用python脚本会有一定的性能损失,不过在对性能要求不是十分高的条件下是可以接受的。

  • 执行test.pyd文件,运行结果如下:

从结果可以看出调用pyd比原生的py文件执行还要快,所以可以使用pythonnet来执行pyd文件,即实现代码保护又提升了执行效率。

C#/.net程序调用python的更多相关文章

  1. winfrom桌面程序调用python解释器

    Winfrom桌面程序调用python解释器执行py脚本后台执行完成具体的功能,为什么要这样处理呢?因为我现在的大部分过项目都是后台的脚本处理,界面基本的输入完成之后,将参数按照规则传入到脚本的入口, ...

  2. windows下使用批处理文件调用python程序

    这个随笔涉及到几个批处理脚本得知识点. windows的start命令, 启动另一个窗口运行指定的程序或命令. windows的call命令, 从批处理程序调用另一个程序, 直到被调用程序退出, 再继 ...

  3. java调用python程序以及向python程序传递参数

    在做项目的时候,经常会碰到这个问题,主要程序是用java写的,有些功能使用python写的,整个项目需要把java代码和python代码进行整合,在一个项目里面运行,这就涉及到java调用python ...

  4. Java调用Python程序

    最近,需要在应用中,需要使用Java程序去调用Python代码,主要有两种实现方式. 一.使用Jython架包 因为调用的Python代码中需要使用Json对象,开始使用jython2.5.2,发现不 ...

  5. python程序调用C/C++代码

    这篇用来记录在些模拟Canoe生成CAN数据桢工具时遇到的问题, 生成CAN数据桢,主要分为两个关注点: 1.如何从can信号名获取到can信号的ID长度以及信号的起始位,并将信号值按照一定的规则填写 ...

  6. Python Module_subprocess_子进程(程序调用)

    目录 目录 前言 软件环境 认识subprocess Popen Constructor构造函数 Class Popen的参数 args 调用程序 调用Shell指令 stdinstdoutstder ...

  7. Autocad中使用命令来调用python对Autocad二次开发打包后的exe程序

    在Autocad中直接调用Python二次开发程序是有必要的,下面介绍一种方法来实现这个功能: 其基本思路是:先将二次开发的程序打包为可执行程序exe,然后编写lsp文件,该文件写入调用exe程序的语 ...

  8. Runtime.getRuntime().exec()实现Java调用python程序

    使用Runtime.getRuntime().exec()来实现Java调用python,调用代码如下所示: import java.io.BufferedReader; import java.io ...

  9. jenkins发布程序触发shell调用python脚本刷新akamai cdn api

    刷新cdn的流程:jenkins获取git中的代码,触发脚本推送到生产环境中(即cdn的源站) --> 触发脚本获取git工作目录的更新列表,将更新列表拼凑成带域名信息的url,写入到目录中 - ...

  10. [DEBUG] java中用Runtime调用python 简单程序输出null

    今天需要在java中调用python脚本,首先考虑的是java自带的Runtime 在ubuntu和win10下分别测试,发现win10报错 java源代码 @Test public void tes ...

随机推荐

  1. FP6397S5 高效、高频同步DC-DC降压变频器IC

    FP6397是一种高效.高频同步DC-DC降压变频器.100%占空比功能提供了低退出操作,延长了便携式系统的电池寿命. 内部同步开关提高了效率,并消除了对外部肖特基二极管的需要.在停机模式下,输入电源 ...

  2. java中使用apache poi 读取 doc,docx,ppt,pptx,xls,xlsx,txt,csv格式的文件示例代码

    java使用apache poi 读取 doc,docx,ppt,pptx,xls,xlsx,txt,csv格式的文件示例代码 1.maven依赖添加 在 pom 文件中添加如下依赖 <depe ...

  3. 前后端开发必会的 HTTP 协议“十全大补丸”(万字长文)

    本文全面介绍了 HTTP 协议相关知识,包括 HTTP 请求报文.响应报文.持久连接.缓存.Cookie 以及 HTTP 版本升级等! HTTP 协议全称为 HyperText Transfer Pr ...

  4. 可视化—AntV G6 高亮相邻节点的两种方式

    目录 内置的高亮节点 自定义高亮 自定义高亮时保持原始颜色 总结 案例完整代码 通过官方文档,可知高亮相邻节点分为两种方法,文档描述并不是很清楚,对刚接触这个库的小白并不是很友好,慢慢总结慢慢来吧 内 ...

  5. eclipse启动一个Springboot项目

    1.准备一个Springboot项目 2.配置好maven 注:本地的maven-repository默认路径是在系统盘的.m文件夹.如果想要修改可参考: eclipse修改maven仓库的位置_本本 ...

  6. 真正“搞”懂HTTP协议07之body的玩法(实践篇)

    我真没想到这篇文章竟然写了将近一个月,一方面我在写这篇文章的时候阳了,所以将近有两周没干活,另外一方面,我发现在写基于Node的HTTP的demo的时候,我不会Node,所以我又要一边学学Node,一 ...

  7. PowerUsageSummary.java源码分析

    在在线网站http://androidxref.com/上对Android版本6.0.1_r10源码进行分析 官方手机的应用耗电排行具体实现位置在:/packages/apps/Settings/sr ...

  8. 梯度下降算法 Gradient Descent

    梯度下降算法 Gradient Descent 梯度下降算法是一种被广泛使用的优化算法.在读论文的时候碰到了一种参数优化问题: 在函数\(F\)中有若干参数是不确定的,已知\(n\)组训练数据,期望找 ...

  9. Linux基础操作-01

    Linux操作系统 很多IC设计的EDA工具都是在Linux系统中进行运行的,在IC设计的从业者都需要熟练掌握Linux操做.可以不掌握Linux原理,但是操作要熟练. Linux 操作系统的特点 多 ...

  10. 算法之Dijkstra及其堆优化和SPFA:图上单源最短路径神器

    签到题-- 题目传送门 SPFA算法 本人曾经写过一篇有关Bellman-ford的博,但就算是挂了优化的ford也只能过这道题的弱化版. 今天就先填个坑,先讲SPFA. 在这里我直接认为你们已经有一 ...