赞
踩
C#调用python函数的常用使用方法有利用Pythonnet、ironPython、打包exe、直接调用、打包成dll等方法
目录
ironPython3现在只在github上有源码,需要自己编译(我尝试失败),但是意外发现ironPython2也可以支持py3
ironPython2的安装十分简单,安装和简单使用可以参考:.NET C# 调用python代码 简单实例_二等碗-CSDN博客_.net 调用python
也可以直接去官网下载,exe引导式安装,相当方便
调用的代码参考:
C#:
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Data;
- using System.Drawing;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- using System.Windows.Forms;
- using Microsoft.Scripting.Hosting;
- using IronPython.Hosting;
-
- namespace WindowsFormsApp8
- {
- public partial class Form1 : Form
- {
- public Form1() => InitializeComponent();
-
-
- private void button1_Click(object sender, EventArgs e)
- {
-
- if (textBox1.Text==string.Empty)
- {
- textBox1.Text = "0";
- }
- if (textBox2.Text == string.Empty)
- {
- textBox2.Text = "0";
- }
-
-
- int num1 = Convert.ToInt32(textBox1.Text);
- int num2 = Convert.ToInt32(textBox2.Text);
-
-
- //调用python
- ScriptRuntime pyRunTime = Python.CreateRuntime();
- dynamic obj = pyRunTime.UseFile("C:/Users/Lee/Desktop/States/test.py");
- label_result.Text = Convert.ToString(obj.add(num1,num2));
- }
-
- private void button2_Click(object sender, EventArgs e)
- {
- textBox1.Text = "0";
- textBox2.Text = "0";
- label_result.Text = "?";
- }
- }
- }
python:
- import sys
-
-
- def hello():
- return 'hello C#'
-
-
- def add(a, b):
- return (a + b)
-
但是ironPython配置简单,但是对py3支持的不是很好,对三方库的使用需要额外配置,并且不支持pyd文件
具体如何配置第三方库可以参考:c#利用IronPython调用python的过程种种问题 - monkeyfx - 博客园
C#调用Python脚本并使用Python的第三方模块_dfskchfk72506的博客-CSDN博客
但是连接中使用的egg文件在相应位置是找不到的(如果你使用了pip或者conda安装的第三方库),因此你需要卸载该三方库并通过源码安装,
可以参考:python安装第三方库时生成.egg文件_曲終~的博客-CSDN博客
要使用nuget模块,要求VS版本高一点,之前使用的VS2010不含此功能,2019可以。
参考:python代码加密,打包pyd文件在C#中调用_星空的博客-CSDN博客_pyd文件
1.1安装Cython
pip install cython
1.2写好自己要编译的py函数文件test.py
- def LOVE(a,b):
- return a+b
1.3然后新建setup.py:
- from distutils.core import setup
- from Cython.Build import cythonize
- setup(ext_modules = cythonize("test.py"))#其中test.py是写好的待编译的文件
并在命令行里运行:
python setup.py build_ext --inplace
1.4 正常情况下会收到:
error: Unable to find vcvarsall.bat
如果没报错跳过这一步。
报错是因为VS缺少vcvarsall.bat文件,比较简单的方法是安装VS,但是VS版本和python有对应要求,我使用的是(VS2019,python3.9),还有一点要注意,安装VS时要把下面的选项勾选上:
1.5 重启电脑之后重新进行1.3步骤,会在py文件的同目录下的build\lib.win-amd64-3.9文件夹中得到对应的pyd文件test.cp39-win_amd64.pyd。
1.6 将test.cp39-win_amd64.pyd重命名与py文件相同test.pyd
会将python的环境一起打包,调用时不需要安装python,但是运行速度会相对慢一点。
c#调用python的方法总结_逸梵的博客-CSDN博客_c#调用python(这个链接实测可以跑通)
参考:https://jingyan.baidu.com/article/ed2a5d1f03e60749f6be17d2.html
这是我自己实现的代码
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Data;
- using System.Drawing;
- using System.Linq;
- using System.Text;
- using System.Windows.Forms;
- using System.Diagnostics;
-
- namespace WindowsFormsApplication1
- {
- public partial class Form1 : Form
- {
- public Form1()
- {
- InitializeComponent();
- }
-
- private void button1_Click(object sender, EventArgs e)
- {
- label1.Text = "waiting . . .";
- string num1;
- if (textBox1.Text.Length != 0)
- num1 = textBox1.Text;
- else
- num1 ="0";
-
- string pyexePath = @".\temp.exe";
-
- Process p = new Process();
- p.StartInfo.FileName = pyexePath;//需要执行的文件路径
- p.StartInfo.UseShellExecute = false; //必需
- p.StartInfo.RedirectStandardOutput = true;//输出参数设定
- p.StartInfo.RedirectStandardInput = true;//传入参数设定
- p.StartInfo.CreateNoWindow = true;
- p.StartInfo.Arguments = num1;//参数以空格分隔,如果某个参数为空,可以传入””
- p.Start();
- string output = p.StandardOutput.ReadToEnd();
- p.WaitForExit();//关键,等待外部程序退出后才能往下执行}
- //Console.Write(output);//输出
- textBox1.Text = Convert.ToString(output);
- p.Close();
-
- label1.Text = "finish";
-
-
- }
-
- private void label1_Click(object sender, EventArgs e)
- {
-
- }
-
- private void textBox1_TextChanged(object sender, EventArgs e)
- {
-
- }
- }
- }
注意exe对应的路径,代码中是放在Debug目录下(debug模式运行)
C#与python之间传参是通过String格式的参数传递,对于数组类型的参数特别不友好,为了解决这一问题,上面链接给出的一个解决方案是可以在C#中将数组写入文件,然后将文件地址传递给python,但是无端多了些许IO。传参只能以string的格式进行传递,因此可以借助Json进行参数传递。
但是,有一点要注意:
bash传参不支持引号
这就导致字典换后的Json在exe中转python会报错,而且报错的唯一反馈是:C#中会获取空值。
这需要C#中的插件Newtonsoft.Json,可以在NuGet直接安装。
使用:
- List<double> list = new List<double>();
- list.Add(0.7);
- list.Add(0.6);
- list.Add(0.3);
-
- JsonSerializer serializer = new JsonSerializer();
- StringWriter sw = new StringWriter();
- serializer.Serialize(new JsonTextWriter(sw), Storage);
- string result = sw.GetStringBuilder().ToString();
就可以将list转化为result,通过调用exe这一小节中介绍的方法进行传参。
传参过程中可能会遇到多个参数类型不同的问题(字典也要求值类型相同)
为此,可以借助如何创建一个在C#中包含不同类型的字典? - 问答 - 云+社区 - 腾讯云介绍的HashTable
完整代码如下:
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Data;
- using System.Drawing;
- using System.Linq;
- using System.Text;
- using System.Windows.Forms;
- using System.Diagnostics;
-
- using System.Text;
- using System.IO;
- using System.Collections;
-
- using Newtonsoft.Json;
-
- namespace WindowsFormsApplication1
- {
-
- public static class StorageExtentions
- {
- public static T Get<T>(this Hashtable table, object key)
- {
- return (T)table[key];
- }
- }
- public partial class Form1 : Form
- {
- public Form1()
- {
- InitializeComponent();
- }
-
-
- private void button1_Click(object sender, EventArgs e)
- {
-
- int a1 = 12;
- string a2 = "test";
- double a3 = 24.1;
-
- double[] a4 = new double[]
- {
- 1,2.3,4.4,6.3,5.5,7.5
- };
-
- Hashtable Storage = new Hashtable();
- Storage.Add("age", a1);
- Storage.Add("name", a2);
- Storage.Add("bmi", a3);
- Storage.Add("grade", a4);
-
-
- JsonSerializer serializer = new JsonSerializer();
- StringWriter sw = new StringWriter();
- serializer.Serialize(new JsonTextWriter(sw), Storage);
- string result = sw.GetStringBuilder().ToString();
-
- MessageBox.Show(result);
-
-
-
- }
-
-
- }
- }
通过这种方式可以将所有参数转化为一个json数据。
这就比较简单,使用
json.loads和json.dumps函数即可,不再赘述。
但是还有一个eval函数,可以是适当使用。
传参出问题很难调试,这是因为传参失败只会返回一个空值,不会有任何提示信息。这种C#调用exe实际上是通过命令行来调用,我们可以先通过在命令行中运行exe文件来判断错误出在exe中还是C#代码中,运行exe的命令为:
exe路径 参数1 参数2 参数3
路径可以是绝对路径或者是相对路径,参数就是exe中接收的参数。如,test.py代码如下,在其生成的exe中的参数有2个
- #test.py
- import sys
-
-
- def func(a, b):
- result = a + b
- return result
-
-
- if __name__ == '__main__':
- print(func(sys.argv[1], sys.argv[2]))
运行该exe的命令就是:test.exe 1 3 (当前处于test.exe所在目录)
- Traceback (most recent call last):
- File "Json_param.py", line 1, in <module>
- File "PyInstaller\loader\pyimod03_importers.py", line 531, in exec_module
- File "numpy\__init__.py", line 230, in <module>
- File "PyInstaller\loader\pyimod03_importers.py", line 531, in exec_module
- File "mkl\__init__.py", line 54, in <module>
- File "mkl\_mkl_service.pyx", line 27, in init mkl._py_mkl_service
- ModuleNotFoundError: No module named 'six'
- [8128] Failed to execute script Json_param
six模块虽然没在代码中使用,但是间接使用,但是pyinstaller打包时并未指定,所以执行时找不到此模块
解决方法:生成exe时生成了一个同名spec文件
在文件的hiddenimports中写入‘six’,然后用该文件重新生成一次exe文件,pyinstaller test.spec。
还有另一种解决方案,pyinstaller打包遇到的一些坑_HQ1356466973的博客-CSDN博客
如果这个东西不是程序必须得,那么打包的时候加入--hidden-import 库名.xxx.xxx,不导入它即可解决:pyinstaller -Fw yourfile.py --hidden-import 库名.xxx.xxx
但是我没有验证过。
另外一种简单的方法是直接在要运行的函数中import缺失的模块(亲测好用)
引号在json中不能传递,转义也不行,现在的解决方法是在传递之前replace替换成指定字符串(我用的是 shuangyinhao_Archer),传递完成后在替换回来,有更好的方法希望不吝赐教。
传参可以同时传多个参数,因此,更好的办法是将每个参数分别Json编码,这样基本不会有引号的问题,但是空格问题还是要单独处理。这里Json编码的作用是保持数组等数据格式,在Python中直接使用eval函数就可以得到原来的形式。
命令行相当不智能,区分不同参数的唯一方法是用空格,因此,在生成json串之前替换成指定字符串或者直接去掉(字典和类中的空格去掉影响不大)
参考:C# 调用 Python(直接调用py代码方式)_coco的专栏-CSDN博客
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。