赞
踩
开发环境:windows, python310, dotnet 6.0
说明:python文件编译成pyd。
1.新建控制台应用程序
2.添加nuget包
3.C#调用代码
- using Python.Runtime;
-
- Runtime.PythonDLL= @"D:\Programs\Python\Python310\python310.dll";
- PythonEngine.Initialize();
- using (Py.GIL())
- {
- dynamic np = Py.Import("PythonTest");
- var dd = np.cal("aa");
- Console.ReadLine();
- }
调试可以看到python脚本返回的代码。
注意:请将PythonDLL路径改为自己的python安装路径;PythonTest为编译好的pyd文件,请将该文件复制到控制台程序debug目录,或者复制到控制台程序里面,将其属性复制到输出目录改为始终复制。
4.附录python脚本
- def cal(param):
- return f'this is from python program, and the parameter is {param}'
******************************************2022-10-13 更新*************************************************
对于简单的py文件上面的方法可以很容易执行,但是对于引用外部package,比如pandas, numpy等等,我们调用的时候会抛出异常 “未找到相应的模块”。仔细一想就会知道,我们只打包了单个py文件,它所依赖的package当然会找不到。下面就是来解决这个问题。
我所测试的环境python版本变成了python 3.8.10。
我们只需要将下面代码放在using(Py.GIL())之前就可以:
- string pathToVirtualEnv = @"D:\Programs\Python\Python3.8.10";
- Environment.SetEnvironmentVariable("PATH", pathToVirtualEnv, EnvironmentVariableTarget.Process);
- Environment.SetEnvironmentVariable("PYTHONHOME", pathToVirtualEnv, EnvironmentVariableTarget.Process);
- Environment.SetEnvironmentVariable("PYTHONPATH", $"{pathToVirtualEnv}\\Lib\\site-packages;{pathToVirtualEnv}\\Lib", EnvironmentVariableTarget.Process);
-
- Runtime.PythonDLL= @"D:\Programs\Python\Python3.8.10\python38.dll";
- PythonEngine.PythonHome = pathToVirtualEnv;
- PythonEngine.PythonPath = PythonEngine.PythonPath + ";" + Environment.GetEnvironmentVariable("PYTHONPATH", EnvironmentVariableTarget.Process);
- PythonEngine.Initialize();
在你执行的过程中如果抛出找不到对应包的异常,你需要在PyCharm里面执行pip install <package-name>。这个命令会将package安装在我们的python安装目录下的Lib\site-package下。
当然你也可以使用指定的路径,具体方法可以参考下面的链接:
Setting Virtual Environment while Embedding Python in C#
Using Python.NET with Virtual Environments
不管哪一种,最重要的是需要在代码里配置PYTHONPATH, 让我们的程序可以有地方去找package。
比如.net core项目,我复制了python安装包的DLLs, Lib文件夹和python38.dll文件到bin\\dubug\\net6.0\\python\python3.8。
只需要将上面代码的pathToVirtualEnv改成新的路径即可:
- //string pathToVirtualEnv = @"D:\Programs\Python\Python3.8.10"
- string pathToVirtualEnv = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location), "Python\\Python3.8");
踩了不少坑,目前稳定运行在生产环境,代码贴出来,可以直接用。
1. ICallPyService, 定义接口
- /// <summary>
- /// 调用python模块服务
- /// </summary>
- public interface ICallPyService
- {
- /// <summary>
- /// 执行方法
- /// </summary>
- /// <param name="model">参数model</param>
- /// <returns>PyObject</returns>
- public string Exec(ModuleInfoModel model);
-
- public void ShutDown();
- }
2. ModuleInfoModel ,定义了调用pyhton代码的基础信息。
- /// <summary>
- /// 模块信息
- /// </summary>
- public class ModuleInfoModel
- {
- /// <summary>
- /// 初始化
- /// </summary>
- public ModuleInfoModel()
- {
- Params = new List<ParamInfo>();
- }
-
- /// <summary>
- /// 导入的模块名
- /// </summary>
- public string ModuleName { get; set; }
-
- /// <summary>
- /// 调用的模块方法
- /// </summary>
- public string MethodName { get; set; }
-
- /// <summary>
- /// 参数列表
- /// </summary>
- public List<ParamInfo> Params { get; set; }
- }
3. ParamInfo, python参数定义(sort是为了定义参数顺序)
- /// <summary>
- /// 参数信息
- /// </summary>
- public class ParamInfo
- {
-
- /// <summary>
- /// 参数顺序
- /// </summary>
- public int Sort { get; set; }
-
- /// <summary>
- /// 参数值
- /// </summary>
- public object Value { get; set; }
- }
4. CallPyService,接口实现
- /// <summary>
- /// python service
- /// </summary>
- public class CallPyService: ICallPyService
- {
- private static string _pythonPath;
-
- /// <summary>
- /// 初始化
- /// </summary>
- public CallPyService()
- {
- if (!App.GetOptions<PythonInfoOptions>().IsLinux)
- {
- _pythonPath = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location), Path.Combine("Python", "Python3.8"));
- Environment.SetEnvironmentVariable("PATH", _pythonPath, EnvironmentVariableTarget.Process);
- Environment.SetEnvironmentVariable("PYTHONHOME", _pythonPath, EnvironmentVariableTarget.Process);
- Environment.SetEnvironmentVariable("PYTHONPATH", $"{Path.Combine(_pythonPath, "Lib", "site-packages")};{Path.Combine(_pythonPath, "Lib")}", EnvironmentVariableTarget.Process);
- Environment.SetEnvironmentVariable("PYTHONNET_PYDLL", $"{Path.Combine(_pythonPath, "python38.dll")}", EnvironmentVariableTarget.Process);
- }
- }
-
- /// <summary>
- /// 执行python模型算法
- /// </summary>
- /// <param name="model"></param>
- /// <returns>PyObject</returns>
- public string Exec(ModuleInfoModel model)
- {
- Init();
- string result;
- using (var gil = Py.GIL())
- {
- PyObject func = Py.Import(model.ModuleName);
- dynamic json = Py.Import("orjson");
- PyObject[] pyParams = model.Params.OrderBy(o => o.Sort).Select(o => (PyObject)EvalObject(o.Value, json)).ToArray();
- PyObject data = func.InvokeMethod(model.MethodName, pyParams);
- data = json.dumps(data, option: json.OPT_SERIALIZE_NUMPY).decode();
- result = data.ToString();
- }
- return result;
- }
-
- private static void Init()
- {
- lock (_lockObj)
- {
- if (!PythonEngine.IsInitialized)
- {
- if (!App.GetOptions<PythonInfoOptions>().IsLinux)
- {
- PythonEngine.PythonHome = _pythonPath;
- PythonEngine.PythonPath = PythonEngine.PythonPath + ";" + Environment.GetEnvironmentVariable("PYTHONPATH", EnvironmentVariableTarget.Process);
- }
- else
- {
- Runtime.PythonDLL = App.GetOptions<PythonInfoOptions>().LinuxPythonDLL;
- }
- PythonEngine.Initialize();
- PythonEngine.BeginAllowThreads();
- }
- }
- }
-
- /// <summary>
- ///
- /// </summary>
- public void ShutDown()
- {
- PythonEngine.Shutdown();
- }
-
- /// <summary>
- /// 将C#对象转成PyObject
- /// </summary>
- /// <param name="value">object</param>
- /// <param name="orjson">orjson</param>
- /// <returns>PyObject</returns>
- private PyObject EvalObject(object value, dynamic orjson)
- {
- if (value == null || value.ToString() == "None")
- {
- return PyObject.None;
- }
- return orjson.loads(JsonConvert.SerializeObject(value));
- }
- }
上面封装成了服务,所以不管是接口还是其他地方直接调用都很方便。后面因为打包成了docker,部署到linux,所以加了linux相关初始化的东西(主要就是路径区别),用不到Linux系统的可以自己修改代码,删掉无用信息。
另外,服务实现了ShutDown,因为执行完python代码后,内存好像没释放,如果需要,可以手动调用ShutDown来释放内存(多线程要不要执行,至少要确保所有脚本都执行完了)。
转载请注明出处,谢谢!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。