之前一直工作在.NET环境下使用C#,在稍微接触了Python以后被迷住了,实际上到现在除了一个演示滤波的小玩具其他什么也没做,不过参考之前的一些经历,觉得把环境搭好比较重要,方便以后随时地开始玩(造)。
在经历了开始的兴奋阶段后,开始琢磨我可以用这个做些什么,当然对我日常工作很有帮助,但是鉴于我的工作大部分时间要在客户的工作站上完成,所以有了以下需求:
1.需要打包成exe部署到客户端。
2.需要可以和C#交互以便复用之前的一些工作,避免重复开发引起的功能不兼容,以及使用对方没有的功能。
解决方案有3个(未完全测试):
1.IronPython,在.NET下实现Python,这样可以在Python中使用所有的.NET库,当然支持所有Python语法。
2.Microsoft Scripting功能以及IronPython.dll,实际上是在.NET程序中加入了脚本支持功能。
3.CPython+Python for .NET,实际上通过一个接口可以在Python中调用.NET下的CLR,最后再用py2exe打包。
先声明:环境搭建好后还没有经过完全测试,没办法保证可以部署到其他设备上正常使用。会慢慢找机会测试。
方案一——将IronPython集成到.NET开发环境:
首先安装IronPython,安装好后可以使用自带的或其他IDE,我试了Eclipse+PyDev,和SharpDevelop,PyDev不支持可视化图形界面,而SharpDevelop没办法加BreakPoint(不知道是不是我不会设置,求大神帮助)。
一个简单的例子如下:
import sys sys.path.append("C:\Program Files (x86)\IronPython 2.7\Lib") sys.path.append("C:\\Program Files (x86)\\IronPython 2.7\\lib\\site-packages") sys.path.append(r"C:\Program Files (x86)\IronPython 2.7") sys.path.append(r"C:\Program Files (x86)\IronPython 2.7\DLLs"); sys.path.append(r"C:\Python27\Lib\site-packages") import clr clr.AddReference('mtrand.dll') clr.AddReference('System.Windows.Forms') clr.AddReference('System.Drawing') #clr.AddReference('SPInterface') clr.AddReference('System.IO') clr.AddReference('System') from System.Diagnostics import Trace from System import Console #import SPInterface as SP import numpy as np print 'Hello World'
如果有多种语言的经验,这里比较好理解
sys.path.append类似C编译时的头文件Include路径
clr.AddReference就是.NET里的添加引用
而from System.Diagnostics import Trace则是类似于C#里的
using System.Diagnostics
里面注释掉的几行是我在C#之前写的库,经测试可以正常使用(而且我的库依赖mathdotnet,在同文件夹下貌似自动识别了)。
优点:比较方便,更偏向于.NET,虽然使用的是Python语法。可以很方便的搭建界面。
缺点:由于不兼容CPython,所以很多库用不了,numpy刚刚支持没多久,而matplotlib不支持,实际上感觉完全不能发挥Python的优点。
方案二——在.NET开发环境使用Python脚本:
第一步同上,安装IronPython,在安装文件夹下有Platforms\Net40文件夹,在.NET项目中引用其中的Microsoft.Scripting.dll和IronPython.dll,可以去网上搜搜例子,就可以在代码调用Python脚本了,一个简单的例子:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using IronPython.Hosting; namespace testIron2 { public partial class Form1 : Form { Microsoft.Scripting.Hosting.ScriptEngine engine; public Form1() { InitializeComponent(); var source = @" import clr clr.AddReference('mtrand.dll') def SysPath(): import sys return sys.path def ImportNumpy(): import numpy "; engine = Python.CreateEngine(); var sp = engine.GetSearchPaths(); sp.Add(@"C:\Program Files (x86)\IronPython 2.7"); sp.Add(@"C:\Program Files (x86)\IronPython 2.7\DLLs"); sp.Add(@"C:\Program Files (x86)\IronPython 2.7\Lib"); sp.Add(@"C:\Program Files (x86)\IronPython 2.7\Lib\site-packages"); engine.SetSearchPaths(sp); var scope = engine.CreateScope(); var ops = engine.Operations; engine.Execute(source, scope); dynamic py = scope; // Calling into IronPython works fine + sys.path looks correct dynamic syspath = py.SysPath(); // EXCEPTION when we try to "import numpy": // "The type initializer for 'NumpyDotNet.NpyCoreApi' threw an exception.", // "Unable to load DLL 'NpyAccessLib': The specified module could not be found. (Exception from HRESULT: 0x8007007E)" dynamic numpy = py.ImportNumpy(); } private void button1_Click(object sender, EventArgs e) { engine.Execute(textBox1.Text); } } }
这段代码是当时在网上找的,很抱歉忘记原始链接了,里面的注释应该是原作者遇到的问题,我遇到了应该是相同的,异常信息是无法读取库,原因是没有mtrand.dll文件,stack overflow上有解答,貌似是因为C编译库的问题,所以要在Python命令字符串中加入
clr.AddReference('mtrand.dll')
可以看出来,和方案一其实是一样的,所以优点是熟悉C#代码的可以尽量的用C#,脚本只是辅助方便现场调试修改。
貌似所有在IronPython下安装的库会有DLL版,这意味着我在纯C#下可以用numpy了?还没有测试。
缺点:所有方案一的缺点,而且Python代码无法调试,只有看异常信息然后从头运行。
方案三:在有.NET环境下安装Python for .NET
这个是目前看起来最方便的,只需要装一个库,就可以用类似IronPython的方式来调用.NET环境,包括我自己的库。同时保留所有之前的功能。简单例子如下:
# encoding: utf-8 ''' Created on 2014年5月14日 @author: zcxsun ''' # import sys #sys.path.append(r"C:\Qt\Projects\sp_reader\sp_reader\bin\Debug") import clr # clr.AddReference('SPInterface') from System import String #import SPInterface as SP a = String(' a,b,c,d,e,f,g ') b = a.Trim() c = String(b).Split(',') for i in c: print i # SP.SP_Path.folder_name = r'C:\Users\Public\Documents\Zeiss\CALYPSO\sp_filter\conf\spiConf.xml' # SP.SPI.init() # for i in SP.SPI.elements: # print i.identifier print 'finish'
注释部分是调用我自己库的代码,需要注意的是很多地方需要显式转换,所以不能用类似a.Trim().Split(‘,’)这样的语法,只能String(a.Trim()).Split(‘,’)这样。因为用type(String(‘ a,b,c ‘).Trim())得到的类型不是Trim应该返回的String类型,而是<type ‘unicode’>
这个感觉应该是最方便的,在Eclipse下显示良好,调试良好。不确定的是py2exe后效果如何。
结论:目前感觉方案2针对我的情况是最优的,方案3则是用来实现功能测试用最方便的,所以未来的短期计划是用PyDev调用一些自己的库,用来设计和实现算法和功能。实验好了后在C#中实现,然后将脚本功能放在文件中调用。
当然,这样的性能应该是有一定问题的,不过对于我的情况是够了。