IronPython初体验
介绍
在 C# 程序中嵌入 IronPython 得到了很好的支持。在本教程中,我们将展示如何完成这个项目。
首先,我们将展示两个非常基本的例子,说明如何执行一个不导入任何模块的非常简单的脚本。然后,再展示如何执行使用模块的脚本。
在 C# 中执行 Python
第一个例子
我们来创建一个执行Python脚本的 C# 应用程序的简单例子。我们使用 Visual Studio 2017 C# 控制台应用程序模板创建一个新项目。我们称之为PythonScriptExecution1。完整的例子可以从我们的GitHub仓库获得:IronPythonTutorials / CSharpIntegration / PythonScriptExecution1。
项目创建完成后,我们使用 NuGet 包管理器添加 IronPython 包,将其安装到当前项目中。这会将以下程序集添加到项目中:
- IronPython
- IronPython.Model
- IronPython.SQLite
- IronPython.Wpf
- Microsoft.Dynamic
- Microsoft.Scripting
- Microsoft.Scripting.AspNet
- Microsoft.Scripting.Metadata
对于第一个例子,我们调用一个 Python 脚本,它将简单地打印出 “Hello World!”。在控制台上。为了保持它尽可能简单,我们只需将 Python 代码硬编码到一个字符串中,然后使用 CreateScriptSourceFromString 从中创建 Microsoft.Scripting.Hosting.ScriptSource 实例。正如你所看到的,这很容易做,只需要3行代码。
static void Main(string[] args)
{
var pythonEngin = IronPython.Hosting.Python.CreateEngine();
var pythonScripts = pythonEngin.CreateScriptSourceFromString("print'hello world'");
pythonScripts.Execute();
}
控制台输出
hello world
如果你想了解更多关于在幕后发生的事情,你可以看看 IronPython Internals Foundations tutorial.
第二个例子
第二个例子与第一个例子几乎相同,但是我们将使用 CreateScriptSourceFromFile 函数从文件中加载脚本,而不是将其硬编码到一个字符串中。由于我们将脚本放在与 Program.cs 文件相同的目录中,我们需要当从 Visual Studio 执行程序时,会出现两个目录。这就是为什么我们脚本的路径是.. \ .. \ HelloWorld.py。您当然可以将脚本放在与可执行文件相同的目录中。代码如下所示。执行程序时,输出当然与前面的示例相同。
完整的例子可以从我们的GitHub仓库获得:IronPythonTutorials/CSharpIntegration/PythonScriptExecution2。
print('Hello World')
static void Main(string[] args)
{
var pythonEngin = IronPython.Hosting.Python.CreateEngine();
var pythonScripts = pythonEngin.CreateScriptSourceFromFile("..\\.\\HelloWorld.py"));
pythonScripts.Execute();
Console.ReadKey();
}
库
搜索路径
通常,Python 脚本将依赖于某个模块,或者是一个自定义模块或 Python 标准库中的模块。我们将展示如何使用标准库,但是考虑到大量的模块,我们将从一个更基本的例子开始。
处理模块的唯一困难是设置引擎将查找模块的路径列表。该ScriptEngine的类提供了一个函数来检索的搜索路径当前列表:GetSearchPaths,另一个设置列表:SetSearchPaths。SetSearchPaths 替换现有的列表,所以如果你想添加一个搜索路径,你将需要首先获取当前列表,添加新的路径,然后将更新的列表传递给 SetSearchPaths 函数。
我们来举例说明一个简单的例子。我们修改之前的的一个示例,以便在 HelloWorld.py 导入另一个名为 HelloWorldModule.py 的模块。我们把这两个文件放在与Program.cs相同的目录下。这两个文件的来源如下所示。
完整的例子可以从我们的GitHub仓库获得:IronPythonTutorials/CSharpIntegration/PythonScriptExecution3。
HelloWorldModule.py
def PrintHelloWorld():
print("Hello World")
HelloWorld.py
import HelloWorldModule
HelloWorldModule.PrintHelloWorld()
static void Main(string[] args)
{
var pythonEngin = IronPython.Hosting.Python.CreateEngine();
Console.WriteLine("Search Paths:");
var searchPaths = pythonEngin.GetSearchPaths();
foreach (var item in searchPaths)
{
Console.WriteLine(item);
}
Console.WriteLine();
searchPaths.Add("..\\..");
pythonEngin.SetSearchPaths(searchPaths);
var pythonScript = pythonEngin.CreateScriptSourceFromFile("..\\..\\HelloWorld.py");
pythonScript.Execute();
}
显然,这是一个稍微做作的例子,因为你通常会把脚本放在一个更合理的位置,但是你应该明白这个想法。
如果一切正常,你应该得到以下输出。
Search Paths:
.
C:\Users\hippieZhou\Documents\Projects\IronPythonTutorials\03_PythonScriptExecution3\bin\Debug\Lib
C:\Users\hippieZhou\Documents\Projects\IronPythonTutorials\03_PythonScriptExecution3\bin\Debug\DLLs
Hello World
但是,如果由于某种原因无法找到一个模块,你会得到下面的异常抛出。
Unhandled Exception: IronPython.Runtime.Exceptions.ImportException: No module na
med os
at Microsoft.Scripting.Runtime.LightExceptions.CheckAndThrow(Object value)
at Microsoft.Scripting.Interpreter.FuncCallInstruction`2.Run(InterpretedFrame
frame)
at Microsoft.Scripting.Interpreter.Interpreter.Run(InterpretedFrame frame)
at Microsoft.Scripting.Interpreter.LightLambda.Run1[T0,TRet](T0 arg0)
at IronPython.Compiler.RuntimeScriptCode.InvokeTarget(Scope scope)
at IronPython.Compiler.RuntimeScriptCode.Run()
at Microsoft.Scripting.Hosting.ScriptSource.Execute()
at PythonScriptExecution3.Program.Main(String[] args) in c:\p4client2\Tutoria
ls\Development\IronPython\Examples\CSharpIntegration\PythonScriptExecution3\Pyth
onScriptExecution3\Program.cs:line 16
让我们仔细看一下搜索路径的初始列表。
默认情况下,当前工作目录将包含在搜索路径列表中。但是,如果您依赖于此,您的应用程序将会工作与否,具体取决于用户启动应用程序时当前的工作目录。在默认情况下,IronPython 将在搜索路径中包含两条与应用程序本身安装位置相关的路径:在上面的输出中可以看到的Lib和DLL路径。这些位置是将模块与主应用程序保持在一起的好选择。
IronPython 实现使用 Assembly.GetEntryAssembly() 函数来获取主机的路径,以便添加 “Lib” 和 “DLL” 路径。有些情况下,Assembly.GetEntryAssembly()将返回 null,这些路径将不会被添加。一个这样的情况是,当环境是 ASP.NET。
标准库
在您的应用程序中使用标准库并不困难。包含标准库的单独的NuGet包可用。这个包将所有的标准库模块添加到 Visual Studio 项目中。出现的问题是,应用程序使用的模块需要与它分发。如何做到这一点取决于具体情况。在最简单的情况下,您可以将所需的模块放在与应用程序二进制文件相同的目录中,并将它们一起分发。如果您选择该解决方案,则默认搜索路径应该足够,因为它包含“。” 目录。
现在让我们来看一个使用标准库的脚本的简单例子。完整的例子可以从我们的 GitHub 仓库获得:IronPythonTutorials/CSharpIntegration/PythonScriptExecution4。
使用 NuGet 获取 IronPython 标准库:IronPython.StdLib
HelloWorldBase64.py
import base64
originalString = b"Hello World!"
print("OriginalString:" + str(originalString))
encodedString = base64.b64encode(originalString)
print("EncodedString:" + str(encodedString))
decodedString = base64.b64decode(encodedString);
print("Decoded String:" + str(decodedString))
C#
static void Main(string[] args)
{
var pythonEngin = IronPython.Hosting.Python.CreateEngine();
Console.WriteLine("Search paths:");
var searchPaths = pythonEngin.GetSearchPaths();
foreach (var path in searchPaths)
{
Console.WriteLine(path);
}
Console.WriteLine();
searchPaths.Add("..\\..\\Lib");
pythonEngin.SetSearchPaths(searchPaths);
var pythonScript = pythonEngin.CreateScriptSourceFromFile("..\\..\\HelloWorldBase64.py");
pythonScript.Execute();
}
输出
Search paths:
.
C:\Users\hippieZhou\Documents\Projects\IronPythonTutorials\04_PythonScriptExecution4\bin\Debug\Lib
C:\Users\hippieZhou\Documents\Projects\IronPythonTutorials\04_PythonScriptExecution4\bin\Debug\DLLs
OriginalString:Hello World!
EncodedString:SGVsbG8gV29ybGQh
Decoded String:Hello World!
共享变量
在 Microsoft.Scripting.Hosting.ScriptScope 类用于保存的是当前在范围内的变量及其关联值列表。本 ScriptScope 类提供的方法来设置,获取和范围删除变量。他们是 SetVariable, GetVariable 和 RemoveVariable。要获取范围中所有变量的列表,请使用GetVariableNames 方法。
在我们最开始的例子中,我们使用 pythonScript.Execute(); 来运行脚本。无参数 Execute() 函数在 ScriptScope 内部创建实例,因此调用者无法访问它。但是,我们可以使用其他重载来创建 ScriptScope 自己并将其传递给 Execute() 函数。
以下示例显示了如何使用这些函数。完整的例子可以从我们的GitHub仓库获得:IronPythonTutorials/CSharpIntegration/PythonScriptExecution5。
Program.cs
static void Main(string[] args)
{
var pythonEngin = IronPython.Hosting.Python.CreateEngine();
var pythonScript = pythonEngin.CreateScriptSourceFromString(
"helloWorldString='Hello World!'\n" +
"print(helloWorldString) \n" +
"print(extrnalString)");
var scope = pythonEngin.CreateScope();
scope.SetVariable("extrnalString", "How are you.");
pythonScript.Execute(scope);
Console.WriteLine();
Console.WriteLine("List of variables in the scope:");
foreach (var name in scope.GetVariableNames())
{
Console.Write(name+ " ");
}
Console.WriteLine();
Console.WriteLine("Variable values:");
Console.WriteLine("helloWorldString:" + scope.GetVariable("helloWorldString"));
Console.WriteLine("extrnalString:" + scope.GetVariable("extrnalString"));
Console.ReadKey();
}
输出
Hello World!
How are you.
List of variables in the scope:
extrnalString __builtins__ __file__ __name__ __doc__ helloWorldString
Variable values:
helloWorldString:Hello World!
extrnalString:How are you.
在这个例子中,脚本定义了这个 helloWorldString 变量,并使用了一个 externalString 在脚本中没有定义的变量 。然后打印这两个变量。
该 externalString 变量显示了C# 代码如何使用该 SetVariable 方法将变量添加到脚本可以使用的范围。
脚本执行后,范围包含由脚本添加的变量列表。C# 代码使用我们前面提到的各种函数来打印执行后的范围内的内容。
导入模块
在本教程前面,我们看到了 Python 脚本如何使用 Python import 语句,只要搜索路径设置正确,就可以像任何常规的 Python 脚本一样使用Python 语句。在这里我们提出另一个有趣的方法,即从 C# 代码中导入模块,而不是 Python 代码。
静态 IronPython.Hosting.Python.ImportModule 函数可以用来导入一个模块。它返回 ScriptScope 包含导入模块中所有变量的类的一个实例。该 ScriptScope 在上面有解释。例如,您可以使用返回的作用域并将其传递给 ScriptSource.Execute 函数,以执行另一个可以使用导入模块的功能的 Python 脚本,甚至可以使用它直接从 C# 执行 Python 方法,如下面的示例所示。
将 ImportModule 搜索路径中的模块作为 Python import 语句进行查找将会这样做,重要的是正确设置路径或找不到模块。
以下示例显示了如何在 Python 模块中定义的函数可以像 C# 函数一样执行。
HelloWorldModule.py
def PrintHelloWorld():
print("Hello World!")
def PrintMessage(message):
print(message)
def Add(arg1,arg2):
return (arg1 + arg2)
Program.cs
static void Main(string[] args)
{
var pythonEngin = IronPython.Hosting.Python.CreateEngine();
var searchPaths = pythonEngin.GetSearchPaths();
searchPaths.Add("..\\..");
pythonEngin.SetSearchPaths(searchPaths);
var scope = IronPython.Hosting.Python.ImportModule(pythonEngin, "HelloWorldModule");
dynamic printHelloWorldFunction = scope.GetVariable("PrintHelloWorld");
printHelloWorldFunction();
dynamic printMessageFunction = scope.GetVariable("PrintMessage");
printMessageFunction("GoodBye!");
dynamic addFunction = scope.GetVariable("Add");
Console.WriteLine("The sum of 1 and 2 is " + addFunction(1,2));
Console.ReadKey();
}
总结
官网给的示例教程是 Visual Studio 2013 + python 2.x 版本的,对于 Visual Studio 2017 + Python 3.X 版本的使用方式影响不大。按照官网描述一步一步还是可以完成整个的基本教程。
个人理解:IronPython 其实就是相当于将 Python 编译成字节码,然后通过 IronPython 创建的虚拟 Python 运行环境(类似虚拟机)从而达到能够运行 Python 的目的。经过个人(不科学的)测试,这种模式的运行效率并不是很高,在 Python 慢的基础上还要慢一个节拍。所以,想在生产环境中使用的话需要慎重考虑。
参考
IronPython初体验的更多相关文章
- IronPython初体验和实战集合等类型转换和类型匹配
人老了,做什么都累~学到了这么多技术,从最早C到C++再到JAVA再到C#,最终都是为了改善产品,改善系统的可维护性.众所周知C#,C++,C都是强类型语言,什么情况都得定义一些实体类来改变业务模型, ...
- .NET平台开源项目速览(15)文档数据库RavenDB-介绍与初体验
不知不觉,“.NET平台开源项目速览“系列文章已经15篇了,每一篇都非常受欢迎,可能技术水平不高,但足够入门了.虽然工作很忙,但还是会抽空把自己知道的,已经平时遇到的好的开源项目分享出来.今天就给大家 ...
- Xamarin+Prism开发详解四:简单Mac OS 虚拟机安装方法与Visual Studio for Mac 初体验
Mac OS 虚拟机安装方法 最近把自己的电脑升级了一下SSD固态硬盘,总算是有容量安装Mac 虚拟机了!经过心碎的安装探索,尝试了国内外的各种安装方法,最后在youtube上找到了一个好方法. 简单 ...
- Spring之初体验
Spring之初体验 Spring是一个轻量级的Java Web开发框架,以IoC(Inverse of Control 控制反转)和 ...
- Xamarin.iOS开发初体验
aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKwAAAA+CAIAAAA5/WfHAAAJrklEQVR4nO2c/VdTRxrH+wfdU84pW0
- 【腾讯Bugly干货分享】基于 Webpack & Vue & Vue-Router 的 SPA 初体验
本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57d13a57132ff21c38110186 导语 最近这几年的前端圈子,由于 ...
- 【Knockout.js 学习体验之旅】(1)ko初体验
前言 什么,你现在还在看knockout.js?这货都已经落后主流一千年了!赶紧去学Angular.React啊,再不赶紧的话,他们也要变out了哦.身旁的90后小伙伴,嘴里还塞着山东的狗不理大蒜包, ...
- 在同一个硬盘上安装多个 Linux 发行版及 Fedora 21 、Fedora 22 初体验
在同一个硬盘上安装多个 Linux 发行版 以前对多个 Linux 发行版的折腾主要是在虚拟机上完成.我的桌面电脑性能比较强大,玩玩虚拟机没啥问题,但是笔记本电脑就不行了.要在我的笔记本电脑上折腾多个 ...
- 百度EChart3初体验
由于项目需要在首页搞一个订单数量的走势图,经过多方查找,体验,感觉ECharts不错,封装的很细,我们只需要看自己需要那种类型的图表,搞定好自己的json数据就OK.至于说如何体现出来,官网的教程很详 ...
随机推荐
- EF6学习笔记(六) 创建复杂的数据模型
EF6学习笔记总目录:ASP.NET MVC5 及 EF6 学习笔记 - (目录整理) 本篇原文地址:Creating a More Complex Data Model 本篇讲的比较碎,很多内容本人 ...
- git 依据SHA值提取文件patch
git format-patch -M master //当前分支所有超前master的提交 git format-patch -s SHA值 //此SHA值提交以后的所有PATCH git form ...
- utf-8 decode
摘自 Lua 5.3 源文件 lutf8lib.c /* ** Decode one UTF-8 sequence, returning NULL if byte sequence is invali ...
- 如果你在it院校学习累了,你能干什么?
文章来源i春秋,未经允许不得转载 工具链接https://bbs.ichunqiu.com/portal.php 如果你在国内的it院校累了,有些厌倦了,你该怎么办? 分享一些joke以前 ...
- JS应用实例5:全选、动态添加
HTML代码: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <t ...
- XStream进行xml和bean互转
加入pom <dependency> <groupId>com.thoughtworks.xstream</groupId> <artifactId>x ...
- 排序算法系列:选择排序算法JAVA版(靠谱、清晰、真实、可用、不罗嗦版)
在网上搜索算法的博客,发现一个比较悲剧的现象非常普遍: 原理讲不清,混乱 啰嗦 图和文对不上 不可用,甚至代码还出错 我总结一个清晰不罗嗦版: 原理: 从数组头元素索引i开始,寻找后面最小的值(比i位 ...
- android开发学习——day8
关于UI学习的总结 EditText的练习 MainActivity.java代码 package test.example.com.ch02_button; import android.suppo ...
- 高德地图添加marker及反地理编码获取POI
项目中集成百度.高德.腾讯地图已是司空见惯的事情,今天我总结了一下项目中用到的高德地图常用的功能: 1.展示高德地图并定位显示定位图标: 2.添加实时大头针: 3.反地理编码获取周围兴趣点 效果如下: ...
- DPI 计算及速查表
[来源]ExMobi 二次开发手册 手机屏幕根据密度范围分为五种:低.中.高.超高.超超高,为了确保界面元素在不同的屏幕都能合适的展示,在设计界面元素的 UI 时,UI 工程师建议统一采用 dpi ( ...