<
>

C#/.net程序调用Python的教程分享

2023-03-02 16:11:54 来源:易采站长站 作者:

目录
C#调用python的常见方法使用pythonnet嵌入Python环境及使用第三方库传递对象调用pyd文件执行速度对比

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>

使用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>路径及python版本根据需要自定义。使用Anaconda Prompt,激活虚拟环境conda activate F:\condaenv\env_name本次测试第三方库Numpy(如果需要其他库,安装方法相同),安装Numpypip 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文件步骤如下:

        1.pip>

        2.在.py文件目录下创建setup.py文件

        from distutils.core import setup
        from Cython.Build import cythonize
        
        setup(
        name = "testName",
        ext_modules = cythonize("test.py"), #将test.py文件编译成pyd
        )
        

        3.执行编译命令

        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的教程分享的详细内容,更多关于C#调用Python的资料请关注易采站长站其它相关文章!

暂时禁止评论

微信扫一扫

易采站长站微信账号