介紹 在 C# 程式中嵌入 IronPython 得到了很好的支持。在本教程中,我們將展示如何完成這個項目。 首先,我們將展示兩個非常基本的例子,說明如何執行一個不導入任何模塊的非常簡單的腳本。然後,再展示如何執行使用模塊的腳本。 在 C 中執行 Python 第一個例子 我們來創建一個執行Pyth ...
介紹
在 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 慢的基礎上還要慢一個節拍。所以,想在生產環境中使用的話需要慎重考慮。