在日常企业项目开发中。会遇到很多紧急变化的需求。这就要求开发人员能够快速响应。做出相应的变更并部署。虽说.NET自身通过AppDomain也可实现热部署。但是相对使用脚本语言来说。还是有一些麻烦的。同时呢。为了团队成员的今后发展。我们决定使用 .NET 集成IronPython来实现快速修改某段代码的需求。
IronPython
IronPython官方网站部分截图
IronPython 是一个开源的基于.NET平台的Python解释器。自身可以使用Python支持库。同时也可以使用.NET程序集。使得.NET与Python具有互操作性。现在最新的版本为 2.7.9。兼容Python2.7语法。具体可参考 https://ironpython.net/
.NET集成IronPyhton的必要工作
日常工作中。我们为了开发方便。经常会写很多帮助类。同样。我们在集成IronPython时。可以抽象出一个通用的帮助类来方便我们编写代码。
通用类包含Python解释引擎的创建。脚本代码的加载。.NET与IronPython互操作对象的传递等功能。
加载IronPython解释引擎
ScriptEngine _pythonEngine = Python.CreateEngine(AppDomain.CurrentDomain);// 移除异常钩子。 Python 加载 DLL 时会抛出异常。 IronPython 2.7 BUG。// 防止抛出异常。导致程序中断执行。var pc = HostingHelpers.GetLanguageContext(_pythonEngine) as PythonContext;var hooks = pc.SystemState.Get__dict__()[\"path_hooks\"] as List;hooks.Clear();
基于当前应用程序域创建 ScriptEngine。并将解释器内的异常钩子函数清除。因时间过长。具体出处无法找到。今后找到了补上。
加载脚本代码
public bool LoadDynamicCode(string filePath。 IDictionary<string。 object> pythonCallObjects = null){ var fileInfo = new FileInfo(filePath); try { var findKey = fileInfo.FullName.ToLower(); var source = _pythonEngine.CreateScriptSourceFromFile(filePath。 Encoding.UTF8); var scope = _pythonEngine.CreateScope(pythonCallObjects); source.Execute(scope); if (!_codeCache.ContainsKey(findKey)) { _codeCache.Add(findKey。 scope); } else { _codeCache[findKey] = scope; } } catch (Exception) { return false; } return true;}
通过给定的文件路径。以UTF-8编码读取脚本文件。并创建一个作用域(ScriptScope)加入缓存。在后期使用时。可以避免再次重新加载脚本的工作。
获取脚本内变量
public T GetVariable<T>(string filePath。 string variableName){ var fileInfo = new FileInfo(filePath); return _codeCache[fileInfo.FullName.ToLower()].GetVariable<T>(variableName);}
因为在上边已经加入了缓存。在这里使用加载时的文件路径。需要获取的变量名。即可实现脚本内变量的获取。可以获取属性。函数等用以调用。
.NET中使用IronPython脚本中的变量
Demo.py
# -*- coding: utf-8 -*-CONST = 10 def func(): return \"Hello IronPython\" def add(i。 j): return i + j
.NET调用的代码
class Program { static void Main(string[] args) { var scriptPath = \"Demo.py\"; PythonHelper.Instance.LoadDynamicCode(scriptPath。 new Dictionary<string。 object>()); var constValue = PythonHelper.Instance.GetVariable<int>(scriptPath。 \"CONST\"); Debug.WriteLine($\"CONST = {constValue}\"); Func<string> funcPtr = PythonHelper.Instance.GetVariable<Func<string>>(scriptPath。\"func\"); Debug.WriteLine($\"func() = {funcPtr()}\"); Func<int。int。int> addPtr = PythonHelper.Instance.GetVariable<Func<int。 int。 int>>(scriptPath。 \"add\"); Debug.WriteLine($\"add(1。1) = {addPtr(1。 1)}\"); } }
GetVariable函数可传入需要获取的内容名称。当获取函数时。可以使用 Action。Func 委托来模拟Python函数的签名。保持一致即可。
IronPython使用.NET的类型
使用.NET内置库
import clr
使用 clr 可导入.NET相关程序集。使用方法与 System.Reflection.Assembly.Load 函数一致。底层代码也就是 Assembly.Load。以下为两种加载方式。更多的方式请参考官方网站。
clr.AddReference(\"System.Windows.Forms\")clr.AddReference(\"System.Xml。 Version=2.0.0.0。 Culture=neutral。 PublicKeyToken=b77a5c561934e089\")
添加完引用后。可使用 import <namespace>或 from <class> import <namespace> 导入需要使用的命名空间。
IronPython使用.NET内置类库获取操作系统版本信息
# -*- coding: utf-8 -*-import clr clr.AddReference(\"System\") from System import Environment print(Environment.OSVersion)
使用自定义类型
还记得在上面写过的 LoadDynamicCode函数吗?第二个参数为 IDictionary<string。 object>类型。这个字典可以将.NET中的类型传入脚本代码中。我们试试吧。
.NET 调用类
class Program { static void Main(string[] args) { var scriptPath = \"Demo.py\"; PythonHelper.Instance.LoadDynamicCode(scriptPath。 new Dictionary<string。 object>() { {\"interactive\"。 new Interactive()} }); Action callNetPtr = PythonHelper.Instance.GetVariable<Action>(scriptPath。 \"call_net\"); callNetPtr(); } } public class Interactive { public int Add(int i。 int j) { return i + j; } }
Demo.py
# -*- coding: utf-8 -*- def call_net(): print(\"From Python。 1 + 1 = %s\" % (interactive.Add(1。1)))
Demo.py 类中的 interactive名称由 LoadDynamicCode 中字典内的Key来指定。
更新IronPython脚本代码
只要将修改后的脚本代码更新。将 LoadDyanmicCode 稍作修改。即可实现每次加载不同版本的脚本代码。以满足快速更新的逻辑。
更多更好玩的IronPython等待你去发掘。快去试试吧!
关注我!了解更多有关编程的知识!也欢迎留言与我讨论!
本文地址:https://gpu.xuandashi.com/77551.html,转载请说明来源于:渲大师
声明:本站部分内容来自网络,如无特殊说明或标注,均为本站原创发布。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。分享目的仅供大家学习与参考,不代表本站立场!