之前一直工作在.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#中实现,然后将脚本功能放在文件中调用。
当然,这样的性能应该是有一定问题的,不过对于我的情况是够了。