• 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:UsershippieZhouDocumentsProjectsIronPythonTutorials3_PythonScriptExecution3inDebugLib
    C:UsershippieZhouDocumentsProjectsIronPythonTutorials3_PythonScriptExecution3inDebugDLLs
    
    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:p4client2Tutoria
    lsDevelopmentIronPythonExamplesCSharpIntegrationPythonScriptExecution3Pyth
    onScriptExecution3Program.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:UsershippieZhouDocumentsProjectsIronPythonTutorials4_PythonScriptExecution4inDebugLib
    C:UsershippieZhouDocumentsProjectsIronPythonTutorials4_PythonScriptExecution4inDebugDLLs
    
    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!'
    " + 
            "print(helloWorldString) 
    " + 
            "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 慢的基础上还要慢一个节拍。所以,想在生产环境中使用的话需要慎重考虑。

    参考

    1. IronPython C# Integration
  • 相关阅读:
    LeetCode——Generate Parentheses
    LeetCode——Best Time to Buy and Sell Stock IV
    LeetCode——Best Time to Buy and Sell Stock III
    LeetCode——Best Time to Buy and Sell Stock
    LeetCode——Find Minimum in Rotated Sorted Array
    Mahout实现基于用户的协同过滤算法
    使用Java对文件进行解压缩
    LeetCode——Convert Sorted Array to Binary Search Tree
    LeetCode——Missing Number
    LeetCode——Integer to Roman
  • 原文地址:https://www.cnblogs.com/hippieZhou/p/9898226.html
Copyright © 2020-2023  润新知