- 在VS,Nuget包管理器搜索“Python.NET”,安装pythonnet包,如下图:
- C#使用Python.NET执行Python脚本文件,C#代码如下:
1 public class PythonExecuter 2 { 3 private readonly string _pythonDllPath; 4 private readonly string _workDir; 5 6 public PythonExecuter(string dllPath, string workDir) 7 { 8 _pythonDllPath = dllPath; 9 _workDir = workDir; 10 } 11 12 public async Task<bool> ExecutePythonScript(string scriptName) 13 { 14 bool result = false; 15 Runtime.PythonDLL = _pythonDllPath; 16 PythonEngine.Initialize(); 17 dynamic sys = Py.Import("sys"); 18 sys.path.append(_workDir); 19 PythonEngine.BeginAllowThreads(); 20 await Task.Run(() => 21 { 22 try 23 { 24 using (Py.GIL()) 25 { 26 Console.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} python脚本准备执行"); 27 dynamic testModule = Py.Import(scriptName);// 导入模块(传入py文件名即可) 28 dynamic py = testModule.main();// 执行该py脚本的main函数 29 Console.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} 执行python脚本成功,返回值:{py}"); 30 result = (int)py == 0; 31 } 32 } 33 catch (Exception ex) 34 { 35 Console.WriteLine(ex.ToString()); 36 } 37 }); 38 // 使用AppContext设置开关来临时启用BinaryFormatter 39 AppContext.SetSwitch("System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization", true); 40 PythonEngine.Shutdown(); 41 AppContext.SetSwitch("System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization", false); 42 43 return result; 44 } 45 }
- 我的Python代码如下(有一个main函数,main函数调用了test函数):
- 调用和执行结果如下:
-
1 PythonExecuter executer = new(@"D:\Python\python313.dll", @"C:\Users\megarobo\PycharmProjects\pythonProject"); 2 bool result = await executer.ExecutePythonScript("pylabrobot"); 3 Console.ReadKey();
- 执行结果包括Python脚本执行的打印输出内容。
- 踩坑点说明:
- 确保在代码中设置了
Runtime.PythonDLL
属性,指向正确的Python DLL文件。例如,如果你的Python版本是3.8,那么DLL文件名通常是python38.dll
(Windows,Python安装根目录下); - 设置工作目录,如果你的Python脚本.py文件在指定“_workDir”目录下(这样你的.py文件才能作为模块被导入),需要调用如下代码:
1 dynamic sys = Py.Import("sys"); 2 sys.path.append(_workDir);
- using (Py.GIL())代码块的作用:在
Python.NET
中,Py.GIL()
用于确保在当前线程中执行Python代码时,GIL被持有,这样可以安全地调用Python代码而不会破坏Python对象的状态。using语句确保在代码块执行完毕后,GIL会被正确释放; - 如果你想在C#多线程中,使用Python对象(如上图我在Task.Run()中运行),需要调用PythonEngine.BeginAllowThreads(),否则无法达到预期效果;
- PythonEngine初始化后,执行完Python脚本后,需要释放资源。但直接调用PythonEngine.Shutdown(),会报错:BinaryFormatter serialization and deserialization are disabled within this application。这是因为 .NET 5 及更高版本出于安全考虑默认禁用了
BinaryFormatter。
Python.NET
在执行PythonEngine.Shutdown()
时依赖于BinaryFormatter。所以我们可以使用
AppContext
设置开关来临时启用BinaryFormatter。