C# 使用Python.NET执行Python脚本文件踩坑总结

  • 在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脚本执行的打印输出内容。
  • 踩坑点说明
  1. 确保在代码中设置了Runtime.PythonDLL属性,指向正确的Python DLL文件。例如,如果你的Python版本是3.8,那么DLL文件名通常是python38.dll(Windows,Python安装根目录下);
  2. 设置工作目录,如果你的Python脚本.py文件在指定“_workDir”目录下(这样你的.py文件才能作为模块被导入),需要调用如下代码:
    1 dynamic sys = Py.Import("sys");
    2 sys.path.append(_workDir);
  3. using (Py.GIL())代码块的作用:在 Python.NET中,Py.GIL() 用于确保在当前线程中执行Python代码时,GIL被持有,这样可以安全地调用Python代码而不会破坏Python对象的状态。using语句确保在代码块执行完毕后,GIL会被正确释放;
  4. 如果你想在C#多线程中,使用Python对象(如上图我在Task.Run()中运行),需要调用PythonEngine.BeginAllowThreads(),否则无法达到预期效果;
  5. PythonEngine初始化后,执行完Python脚本后,需要释放资源。但直接调用PythonEngine.Shutdown()报错:BinaryFormatter serialization and deserialization are disabled within this application。这是因为 .NET 5 及更高版本出于安全考虑默认禁用了 BinaryFormatter。Python.NET 在执行 PythonEngine.Shutdown() 时依赖于BinaryFormatter。所以我们可以使用AppContext设置开关来临时启用BinaryFormatter。
  • 踩坑续!!!(实际工作中,我调用的Python脚本的方法为异步方法)
  • 如果我们调用的Python代码中的方法为异步方法,需要做额外处理(Python代码修改为如下异步代码):
  • 此时,我们的C#调用代码需要做如下修改:

  • 复制代码
     1 await Task.Run(() =>
     2 {
     3     try
     4     {
     5         using (Py.GIL())
     6         {
     7             Console.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} python脚本准备执行");
     8             dynamic asyncio = Py.Import("asyncio");  // 导入异步模块
     9             dynamic pylab = Py.Import(scriptName);   // 导入模块(传入py文件名即可)
    10             dynamic py = asyncio.run(pylab.main());  // 执行该py脚本的异步main函数
    11             Console.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} 执行python脚本成功,返回值:{py}");
    12             result = (int)py == 0;
    13         }
    14     }
    15     catch (PythonException ex)
    16     {
    17         Console.WriteLine(ex.ToString());
    18     }
    19 });
    复制代码

    我们将Python的异步编程模块:"asyncio"导入后,调用 asyncio.run(pylab.main()); 将目标模块的main方法作为参数传入,这样我们才能在python脚本执行之后拿到执行结果,相当于C#同步调用异步方法,等待方法执行完毕(否则会直接返回!!!)。

posted @   星渐渐被你吸引  阅读(550)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示