在.Net Framework中调用Python的脚本方法 (以VB和C#为例)
某个项目中涉及到这样一个情景: VB/C#写的原始项目要调用Python的一些方法完成特殊的操作, 那么这就涉及到了,在.Net Framework中如何调用Python的脚本方法。
具体步骤流程如下所示:
1): 展示一个简单的Python代码,即传递一个参数,然后返回修改后字符串,此文件名称为 mytest.py
def MyTestFunction(name): return "testing " + name
2): 我们借助第三方的工具来实现这个操作,那么这个第三方工具就是 IronPython,IronPython是一种在.Net及Mono上的Python实现,是一个基于微软的DLR引擎的开源项目,可以去这里下载或者跟踪源代码(https://github.com/IronLanguages/ironpython2)。 然后我们可以到这个链接中下载安装文件(https://github.com/IronLanguages/ironpython2/releases/tag/ipy-2.7.8), 安装文件安装完之后我们可以到对应的安装目录下面找到我们所需要的dll(IronPython.dll, IronPython.Modules.dll, Microsoft.Dynamic.dll, Microsoft.Scripting.dll)以及一个名为Lib的文件夹,如下图所示:
3):我们用VS创建一个VB的工程,然后将上面的这四个dll引用进来,之后就可以进行具体的调用了,假设我们的Python代码文件放置的目录是 D:\Code\PyTest\mytest.py 必要时请将此文件copy到VB执行目录中, 然后把对应的pythonPath换掉
如下代码所示:
Imports IronPython.Hosting Imports Microsoft.Scripting.Hosting Public Class Form1 Private Sub CallPython() Dim pythonPath = "D:\Code\PyTest\mytest.py" Dim pyruntime as ScriptRuntime = Python.CreateRuntime() Dim fileObj As Object = pyruntime.UseFile(pythonPath) Dim result = fileObj.MyTestFunction("World") End Sub End Class
这样我们就通过IronPython完成了VB调用Python脚本方法,我们可以继续参考如下连接: https://blog.csdn.net/letunihao/article/details/41985163
这里Python的脚本方法非常单纯无暇,没有任何的其他脚本的引用, 但是实际项目中这样单纯的代码大多是没有意义的,总会引用其他的module来实现更加复杂的逻辑。
下面我们就要进阶了,如果Python脚本之间有相互的引用,如何完成我们的目标呢?请看如下步骤
4):在相同的目录中创建另外一个Python文件来实现文件的读写,文件名为 mytest_common.py,
import os class FileOperator: def WriteInfoToFile(self, path): file = open(path, "w") file.write("Hello World") file.close() def ReadInfoFromFile(self, path): fileInfo = "" data = open(path) for each_line in data: fileInfo += each_line data.close() return fileInfo
为什么实现方法的时候要加入一个额外的参数 self?
请看如下连接: https://stackoverflow.com/questions/23944657/typeerror-method-takes-1-positional-argument-but-2-were-given/42016399
文件读写请参考 (http://www.pythonforbeginners.com/files/reading-and-writing-files-in-python)
5):然后我们对 mytest.py 文件做如下修改,假设我们已经通过Python自己运行WriteInfoToFile方法已经生成了一个Test.txt文件
from mytest_common import FileOperator def MyTestReadInfo(): fInfo = fOperator.ReadInfoFromFile("D:\Code\PyTest\Test.txt") return fInfo fOperator = FileOperator()
6):然后我们用如下VB代码进行对新的Python脚本方法的调用
Imports IronPython.Hosting Imports Microsoft.Scripting.Hosting Public Class Form1 Private Sub CallPython() Dim pythonPath = "D:\Code\PyTest\mytest.py" Dim pyruntime as ScriptRuntime = Python.CreateRuntime() Dim fileObj As Object = pyruntime.UseFile(pythonPath) Dim result = fileObj.MyTestReadInfo() End Sub End Class
会throw exception说:某某某某module无法加载, 或者是找不到某某module。Unhandled Exception: IronPython.Runtime.Exceptions.ImportException: No module named ...
原因是:Python自己运行的时候会自动加载对应相关联的module,特别是一些系统的module,比如这里面的 os, 但是我们通过外部调用的时候无法自动建立这样的链接,因为我们要在Python的源文件中明确指明所引用的那些系统源文件所在folder,让其可以在指定的folder下面去寻找相关联的源文件。
那么这些源文件在什么地方呢?我们可以到Python的安装目录下寻找,也可以到我们第2步 IronPython的安装目录下面寻找,即Lib文件夹,然后将此文件夹copy到我们的测试Python的同级文件夹,用相对路径指定,当然你也可以不用copy,然后用绝对路径定位到Lib文件夹即可
代码如下所示:
import sys sys.path.append(".\Lib") from mytest_common import FileOperator def MyTestReadInfo(): fInfo = fOperator.ReadInfoFromFile("D:\Code\PyTest\Test.txt") return fInfo fOperator = FileOperator()
这样我们再用第6步中的VB代码去调用就可以成功了。
注:使用相对路径时,请注意使用文件的位置,保证能够成功定位到。
更多详细信息可以参考如下连接:
https://stackoverflow.com/questions/6195781/ironpython-exe-compiled-using-pyc-py-cannot-import-module-os
https://thesesergio.wordpress.com/2013/09/11/how-to-generate-and-use-a-exe-that-uses-net-dlls-with-ironpython-pyc-py/
https://blog.csdn.net/letunihao/article/details/41985163
给出C#部分代码以作参考
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public static dynamic GetPythonFileObj() { var fath = AppContext.BaseDirectory + @"Services\PythonService.py"; var pyruntime = Python.CreateRuntime(); var pyengine = pyruntime.GetEngine("Python"); var paths = pyengine.GetSearchPaths(); paths.Add(AppContext.BaseDirectory + @"Services"); paths.Add(AppContext.BaseDirectory + @"Services\Lib"); pyengine.SetSearchPaths(paths); dynamic fileObj = pyruntime.UseFile(fath); return fileObj; } public static string UploadTag(ref dynamic fileObj, string tagName) { var tagValue = fileObj.ex_read(tagName); return tagValue.ToString(); }