如何从C#调用带数组参数的Python函数并获取返回值
C#进程调用Python函数传递数组参数解决方案
核心思路
由于IronPython无法安装numpy、gurobipy等依赖库,采用进程调用+JSON序列化的方式传递数组参数:C#将数组序列化为JSON字符串传递给Python进程,Python端解析JSON还原为numpy数组,执行函数后再将结果序列化为JSON返回给C#。
步骤1:修改Python脚本,支持命令行参数接收数组
修改原Python函数,添加入口逻辑,从命令行读取JSON格式的points数组及其他参数,执行mclp后输出JSON结果:
import sys import json import numpy as np import gurobipy as gp from gurobipy import GRB def mclp(points, K, radius, M): n = points.shape[0] # 计算距离矩阵 dist_matrix = np.sqrt(((points[:, np.newaxis] - points)**2).sum(axis=2)) # 创建模型 m = gp.Model() # 变量 x = m.addVars(n, vtype=GRB.BINARY, name='x') y = m.addVars(n, vtype=GRB.BINARY, name='y') # 目标函数 m.setObjective(gp.quicksum(y[i] for i in range(n)), GRB.MAXIMIZE) # 约束 m.addConstr(gp.quicksum(x[j] for j in range(n)) <= K, name='K') for i in range(n): m.addConstr(gp.quicksum(x[j] for j in range(n) if dist_matrix[i][j] <= radius) >= y[i], name=f'cover_{i}') m.addConstrs(y[i] <= M for i in range(n)) # 求解 m.optimize() # 返回结果 selected = [j for j in range(n) if x[j].X > 0.5] covered = [i for i in range(n) if y[i].X > 0.5] return selected, covered if __name__ == '__main__': # 检查参数数量 if len(sys.argv) < 4: print("参数错误:需传入points数组(JSON字符串)、K、radius、M") sys.exit(1) # 解析参数 points_json = sys.argv[1] points = np.array(json.loads(points_json)) K = int(sys.argv[2]) radius = float(sys.argv[3]) M = int(sys.argv[4]) # 执行函数并输出结果 selected, covered = mclp(points, K, radius, M) print(json.dumps({'selected': selected, 'covered': covered}))
步骤2:C#端实现进程调用与参数传递
使用Process类启动Python进程,将points数组序列化为JSON字符串作为命令行参数传递,捕获输出并解析结果:
using System; using System.Diagnostics; using System.Text; using System.Collections.Generic; using Newtonsoft.Json; // 或使用System.Text.Json,需对应调整序列化代码 class PythonCaller { static void Main() { // 构造输入参数 double[,] points = new double[,] { { 1.2, 3.4 }, { 5.6, 7.8 }, { 9.0, 2.3 }, { 4.5, 6.7 } }; int K = 2; double radius = 4.0; int M = 1; // 将二维数组转为可序列化的List<double[]> var pointList = new List<double[]>(); for (int i = 0; i < points.GetLength(0); i++) { double[] point = new double[points.GetLength(1)]; for (int j = 0; j < points.GetLength(1); j++) { point[j] = points[i, j]; } pointList.Add(point); } string pointsJson = JsonConvert.SerializeObject(pointList); // 配置Python进程 var psi = new ProcessStartInfo { FileName = "python", // 若为Python3,需改为"python3"或绝对路径(如"C:/Python310/python.exe") Arguments = $"\"mclp_script.py\" \"{pointsJson}\" {K} {radius} {M}", RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true, StandardOutputEncoding = Encoding.UTF8, StandardErrorEncoding = Encoding.UTF8 }; using (var process = Process.Start(psi)) { string output = process.StandardOutput.ReadToEnd(); string error = process.StandardError.ReadToEnd(); process.WaitForExit(); // 处理执行错误 if (process.ExitCode != 0) { Console.WriteLine("Python脚本执行失败:"); Console.WriteLine(error); return; } // 解析返回结果 var result = JsonConvert.DeserializeObject<McLpResult>(output); Console.WriteLine("选中的点索引:"); foreach (int idx in result.Selected) Console.WriteLine(idx); Console.WriteLine("\n覆盖的点索引:"); foreach (int idx in result.Covered) Console.WriteLine(idx); } } } // 定义结果接收类 public class McLpResult { public List<int> Selected { get; set; } public List<int> Covered { get; set; } }
关键注意事项
- Python环境配置:确保Python已安装numpy、gurobipy,且C#进程能找到python.exe(建议使用绝对路径避免环境变量问题)。
- 大数组处理:若points数组过大,命令行参数会触发长度限制,此时改为标准输入传递:Python端从
sys.stdin.read()读取JSON,C#端通过process.StandardInput.WriteLine(pointsJson)发送。 - 调试技巧:捕获Python的标准错误输出,可快速定位脚本执行中的异常(如依赖缺失、参数解析错误)。
内容的提问来源于stack exchange,提问作者user2356725
相关产品推荐
相关产品推荐

