C#/.net程序调用python
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
Nuget安装
pythonnet
设置
Runtime.PythonDLL
属性,即pythonxx.dll路径,xx为版本号设置
PythonEngine.PythonHome
,即python.exe所在路径设置
PythonEngine.PythonPath
,python脚本所在目录,可以放置多个路径,以分号隔开,但是pathToVirtualEnv\Lib\site-packages和pathToVirtualEnv\Lib应放在最后调用
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来构建专用的虚拟环境。
创建专用虚拟环境(windows下首先切换到要建立虚拟环境的根目录下),执行
conda create --prefix=F:\condaenv\env_name python=3.7
路径及python版本根据需要自定义。使用Anaconda Prompt,激活虚拟环境
conda activate F:\condaenv\env_name
本次测试第三方库Numpy(如果需要其他库,安装方法相同),安装Numpy
pip 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点作用:
- 安全性更高:通过pyd生成的文件,已变成了dll文件,无法查看源码
- 编译成pyd后,性能会有提升
将.py文件编译成pyd文件步骤如下:
pip install cython
- 在.py文件目录下创建setup.py文件
from distutils.core import setup
from Cython.Build import cythonize
setup(
name = "testName",
ext_modules = cythonize("test.py"), #将test.py文件编译成pyd
)
- 执行编译命令
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的更多相关文章
- winfrom桌面程序调用python解释器
Winfrom桌面程序调用python解释器执行py脚本后台执行完成具体的功能,为什么要这样处理呢?因为我现在的大部分过项目都是后台的脚本处理,界面基本的输入完成之后,将参数按照规则传入到脚本的入口, ...
- windows下使用批处理文件调用python程序
这个随笔涉及到几个批处理脚本得知识点. windows的start命令, 启动另一个窗口运行指定的程序或命令. windows的call命令, 从批处理程序调用另一个程序, 直到被调用程序退出, 再继 ...
- java调用python程序以及向python程序传递参数
在做项目的时候,经常会碰到这个问题,主要程序是用java写的,有些功能使用python写的,整个项目需要把java代码和python代码进行整合,在一个项目里面运行,这就涉及到java调用python ...
- Java调用Python程序
最近,需要在应用中,需要使用Java程序去调用Python代码,主要有两种实现方式. 一.使用Jython架包 因为调用的Python代码中需要使用Json对象,开始使用jython2.5.2,发现不 ...
- python程序调用C/C++代码
这篇用来记录在些模拟Canoe生成CAN数据桢工具时遇到的问题, 生成CAN数据桢,主要分为两个关注点: 1.如何从can信号名获取到can信号的ID长度以及信号的起始位,并将信号值按照一定的规则填写 ...
- Python Module_subprocess_子进程(程序调用)
目录 目录 前言 软件环境 认识subprocess Popen Constructor构造函数 Class Popen的参数 args 调用程序 调用Shell指令 stdinstdoutstder ...
- Autocad中使用命令来调用python对Autocad二次开发打包后的exe程序
在Autocad中直接调用Python二次开发程序是有必要的,下面介绍一种方法来实现这个功能: 其基本思路是:先将二次开发的程序打包为可执行程序exe,然后编写lsp文件,该文件写入调用exe程序的语 ...
- Runtime.getRuntime().exec()实现Java调用python程序
使用Runtime.getRuntime().exec()来实现Java调用python,调用代码如下所示: import java.io.BufferedReader; import java.io ...
- jenkins发布程序触发shell调用python脚本刷新akamai cdn api
刷新cdn的流程:jenkins获取git中的代码,触发脚本推送到生产环境中(即cdn的源站) --> 触发脚本获取git工作目录的更新列表,将更新列表拼凑成带域名信息的url,写入到目录中 - ...
- [DEBUG] java中用Runtime调用python 简单程序输出null
今天需要在java中调用python脚本,首先考虑的是java自带的Runtime 在ubuntu和win10下分别测试,发现win10报错 java源代码 @Test public void tes ...
随机推荐
- 这玩意也太猛了!朋友们,我在此严正呼吁大家:端好饭碗,谨防 AI!
你好呀,我是歪歪. 最近几天大火的 ChatGPT 你玩了吗? 如果你不知道它是个什么东西,那么我让它给你来个自我介绍: 说白了,就是一个可以对话的人工智能. 我开始以为就是一个升级版的"小 ...
- websockets的原理
一.应用场景 http 协议 客户端发起请求的时候才会返回内容,如果要处理类似于聊天室的应用,需要客户端不间断的发起请求(轮询),非常占用服务器的性能.所以websocket出现了. 二.ws(wss ...
- 大数据-业务数据采集-FlinkCDC
CDC CDC 是 Change Data Capture(变更数据获取)的简称.核心思想是,监测并捕获数据库的变动(包括数据或数据表的插入.更新以及删除等),将这些变更按发生的顺序完整记录下来,写入 ...
- java逻辑运算中异或^
本文主要阐明逻辑运算符^(异或)的作用 a ^ b,相异为真,相同为假. 注意,异或运算,还能交换两个变量. int a = 1; int b = 2; System.out.println(&quo ...
- MAUI新生4.6-主题设置LightTheme&DarkTheme
通过主题设置,可以在运行时更改应用的主题外观,比如切换亮色主题和暗黑主题.主题设置并没有新的知识点,基本的原理如下: 定义每个应用主题的ResourceDictionary,每个ResourceDic ...
- 分享自己亲测过的Visualstudio 2019中开发Typescript时,设置自动编译生成js,无需手工运行命令生成的方法。
步骤1)右键web项目,添加 tsconfig.json文件. 步骤2)确保配置如下,编译版本可自行设置,这里主要关注编译目标目录和自动编译设置: { "compileOnSave" ...
- [编程基础] C++多线程入门4-数据共享和资源竞争
原始C++标准仅支持单线程编程.新的C++标准(称为C++11或C++0x)于2011年发布.在C++11中,引入了新的线程库.因此运行本文程序需要C++至少符合C++ 11标准. 4 数据共享和资源 ...
- [C++]bitwise和memberwise的区别
在看<深入探索C++对象模型>这本书的时候,我看见了bitwise senimatics和memberwise senimatics,看的时候还不清楚这两个是什么意思,书本上直接使用的是英 ...
- SOFAJRaft源码阅读-模块启动过程
本篇文章旨在分析SOFAJRaft中jraft-example模块的启动过程,由于SOFAJRaft在持续开源的过程中,所以无法保证示例代码永远是最新的,要是有较大的变动或者纰漏.错误的地方,欢迎大家 ...
- angular---angular路由守卫,有的可以访问有的不可以访问,有的路由地址只有在满足条件时候访问,其它禁止访问,