初试IronPython与.NET的集成

在得知IronPython如今已步入1.0 RC1后兴奋不已,赶快下载下来试用。虽说功能性变化不多(加入了试用性Python 2.5的语法支持:D),但稳定性如今已提高了不少,应该说可以用来投入正式使用了。:)

如何用好IronPython

翻看了一下Tutorials,发现和以前的内容差不多,按照Tutorials中的方法尝试了一下在C#中嵌入IronPython 代码,发现还很不错,最起码可行,虽然速度肯定没有用C#直接写出来的快(因为IronPython需要动态编译的缘故)。

好的,现在我们拥有了一项较稳定的可以投入使用的新技术了,但是如果没有市场,那么这个新技术是不能够生存的,所以我左思右想 IronPython在企业级开发中到底占了什么地位(因为我是做企业级开发的:)),有何等优势呢?通过分析现有IronPython的特征,我得到了一个结论,那就是它比较适合做脚本引擎或者简单的客户端。以下是我分析的IronPython的一些特征:

  1. Python的语法,灵活性相当的高!
  2. 与.NET Framework的交互实现的非常的好,现在用Python的语法也可以用.NET的类库了。
  3. 在其他.NET语言中可以通过引用IronPython.dll来执行IronPython文件并且可以实现内存共享(在同一AppDomain中)。
  4. 纯IronPython程序启动非常慢。
  5. 由于IronPython的特殊性,它所编译出来的程序集中的成员并非你所想象的,所以不能够由其他程序集直接引用使用。这主要还是因为Python是一种动态类型语言,所以无法在编译时给予合适的静态类型(唯一合适的就是System.Object)。另外为了与CPython保持兼容性,IronPython也是以module为单位进行group的,也就是一个module被编译成一个类,这个module中的所有成员,包括class都被当作成员编译在这个类中。

基于以上一些IronPython现有的特征来看,我觉得它更适合使用.NET 程序集,而非为.NET程序提供功能(虽然也是可行的,也是我希望的),所以我建议将IronPython用作客户端以及脚本引擎一类的用途上,因为这类程序是属于consumer类的,它们使用现成的API,但是它们不为其他人提供新的API。

如何用IronPython为.NET程序提供API

读者可能会说我自相矛盾,刚刚才说过IronPython不适合用来提供API,怎么现在又提起这件事来了呢(唉,人本身就是一种很矛盾的生物)?!其实就像上面我在括号中提及的,利用IronPython的交互能力与语言的灵活性为我的.NET程序提供简化的解决方案是我的期望,虽然速度方面还不尽人意,但优化的空间永远都是存在的,我相信随着时间的推移IronPython会在性能方面做的更出色(从这方面来看我是个乐观主义者:D)。

为了简化主程序与IronPython文件的交互与维护难度,我决定将IronPython文件作为服务(service)来看待,IronPython所提供的每一个API就是一个独立的服务,每次需要使用这个服务的时候我只需向它发送相应的消息(message)即可,而它,会返回给我一个回应(response)如果有必要的话。我觉得这样做比较符合目前的SOA潮流。(如果你还不知道SOA是什么的话,一句话告诉你,它是一种企业级集成手段)

我的做法很简单,只是初步实现了一个对IronPython文件调用的封装,示例如下。

 List<int> a = new List<int>();
 a.Add(
1);
 a.Add(
2);
 a.Add(
3);

 List
<int> b = new List<int>();
 b.Add(
4);
 b.Add(
5);
 b.Add(
6);

 PyFacade
<IList<IList<object>>> py =
    
new PyFacade<IList<IList<object>>>();
 py.PythonFile 
= "mayorThenTen.py";
 py.Arguments.Add(
"listA", a);
 py.Arguments.Add(
"listB", b);
 py.Execute();

 
foreach (IList<object> tuple in py.Result) {
    Console.Write(tuple[
0]);
    Console.Write(
"");
    Console.Write(tuple[
1]);
    Console.WriteLine();
 }

PyFacade的代码如下。

using System;
using System.Collections.Generic;

using IronPython.Hosting;

namespace Cavingdeep.Python {
   
public class PyFacade<T> {
      
public const string ResultVariableName = "_result_";

      
private PythonEngine engine = new PythonEngine();

      
private string pythonFile;
      
private Result<T> result = new Result<T>();
      
private IDictionary<string,object> args =
         
new Dictionary<string,object>();

      
public string PythonFile {
         
get {return this.pythonFile;}
         
set {this.pythonFile = value;}
      }


      
public T Result {
         
get {return this.result.Value;}
      }


      
public IDictionary<string,object> Arguments {
         
get {return this.args;}
      }


      
public void Execute() {
         engine.Globals[ResultVariableName] 
= this.result;

         
foreach (KeyValuePair<string,object> arg in this.args) {
            engine.Globals[arg.Key] 
= arg.Value;
         }


         engine.ExecuteFile(
this.pythonFile);
      }

   }

}

Result的代码如下。

using System;

namespace Cavingdeep.Python {
   
public class Result<T> {
      
public T Value;
   }

}

mayorThenTen.py的代码如下,这是这个示例中用到的IronPython文件。

def mayorThenTen(a, b):
   
return [(x, y) for x in a for y in b if x + y > 5]

if globals().has_key('_result_'):
   _result_.Value 
= mayorThenTen(listA, listB)

这里的这个PyFacade是个比较通用的与IronPython交互的接口类,在真正的应用中建议用强类型模式(强类型设计实践)加以封装以得到type safety等好处。

关于PyFacade是如何与IronPython文件交互一题,简单地说就是通过那个_result_变量,它会在IronPython文件中被赋值,这个值是外界可见的,也就是说IronPython文件的执行与当前程序是在一个AppDomain中的。有关这类知识感兴趣的读者可以去研究一下动态编译、加载与程序域(AppDomain)等知识。

posted @ 2006-08-01 11:06  Cavingdeep  阅读(...)  评论(...编辑  收藏