ironpython clr_[集成IronPython] 使CLR对象对动态语言更友好(二)—— 支持切片

动态类型语言(以下简称:"动态语言"),在10年前就已流行起来。JavaScript更是成为了WEB前台开发的事实标准。但它们进入普通开发 人员的视野也就在近几年。随着Web2.0和敏捷开发方法论的兴起,动态语言的灵活高效的特性成为了它被更多项目选择和使用的理由。一些大型网站已开始使 用动态语言来实现,其中,国内比较优秀的作品有“豆瓣”。微软更是不甘落后,建立了DLR(动态语言运行时)来吸引动态语言爱好者在其上实现动态语言。IronPython就是其主要成员之一。

在本系列文章中,我们将逐步实现一个自定义控件,实现类似IDE的Immediate窗口的功能。用户可以在其中输入和运行IronPython代码。 【返回目录】

在上一节中, 我们在一个CLR对象上实现了几个SpecialName方法。实在是一个非常Special的Attribute。不管怎样,你会发现,在IronPython的扩展方面,这个东东将无处不在。

今天,我们将实现一个Collection对象,并且让这个Collection对象支持IronPython的一个最重要的功能——Slice(切片)。

还是先来看效果:

注:大家一定很奇怪,我为什么总是在使用IDE的Output窗口来查看print效果。还记得总目录里面有一章一直没有写吗?呵呵,因为我一直不知道如何重定向IronPython的标准输入输出。因此,只好拿Output窗口充数了。

sad_smile.gif

另外一个需要说明的,大家会发现上面的输出窗口中多了几个空行,这个可能是因为"\r\n"和"\n"的差别引起的。

下面是IronPython的代码:

printTestCollection[0:9:2]

简单来说,“切片”是IronPython在序列类型对象(字符串,列表和元组)上支持的一套特殊的运算符。语法是:sequence[starting_index:ending_index:step]。意思是说,取出序列中从starting_index开始到ending_index结束,每隔step个元素组成的新序列。很实用的一个功能哦。

具体的IronPython的语法不是我们讨论的重点,我们直接切入主题,看看我怎样让我的一个Collection对象支持切片操作。

为了展示,我从System.Collections.ObjectModel.Collection继承了一个Collection对象(这仅仅是为了实现的方便,你完全可以从头开始实现ICollection接口去实现一个你自己的Collection对象)。当我将这个对象添加到我的测试程序中后(新的DLRFriendlyCollection同上一节的DLRFriendlyObject一样,和DLConsole的实现没有直接的关系,因此它们都被定义在了测试工程中)。发现,如果调用切片操作,会产生一个ArgumentTypeException。(我还以为微软会将这个功能直接做在兼容性考虑里面呢)看来还是要们自己来做。

好在IronPython中提供了Slice对象。也就是说,你只需要在你的对象上实现了特定的接口,就可以提供对切片的支持了。让我们先从最基本的入手——取一个切片。

1

None.gifnamespaceTestDynamicLanguageConsole2ExpandedBlockStart.gif

ContractedBlock.gifdot.gif{3InBlock.gifpublicclassDLRFriendlyCollection : Collection, INotifyPropertyChanged4ExpandedSubBlockStart.gif

ContractedSubBlock.gifdot.gif{5InBlock.gifpublicIEnumerablethis[Slice slice]6ExpandedSubBlockStart.gif

ContractedSubBlock.gifdot.gif{7InBlock.gifget8ExpandedSubBlockStart.gif

ContractedSubBlock.gifdot.gif{9InBlock.gifif(slice==null)10ExpandedSubBlockStart.gif

ContractedSubBlock.gifdot.gif{11InBlock.gifthrownewArgumentNullException("slice");12ExpandedSubBlockEnd.gif }13InBlock.gifintstart;14InBlock.gifintstop;15InBlock.gifintstep;16InBlock.gif slice.indices(this.Count,outstart,outstop,outstep);17InBlock.gif18InBlock.gif DLRFriendlyCollection newCollection=newDLRFriendlyCollection();19InBlock.gifif((step>0&&start>=stop)||(step<0&&start<=stop))20ExpandedSubBlockStart.gif

ContractedSubBlock.gifdot.gif{21InBlock.gifreturnnewCollection;22ExpandedSubBlockEnd.gif }23InBlock.gif24InBlock.giffor(inti=start; i

ContractedSubBlock.gifdot.gif{26InBlock.gif newCollection.Add(this[i]);27ExpandedSubBlockEnd.gif }28InBlock.gif29InBlock.gifreturnnewCollection;30ExpandedSubBlockEnd.gif }31ExpandedSubBlockEnd.gif }32ExpandedSubBlockEnd.gif }33ExpandedBlockEnd.gif}

呵呵,很简单,你只需要实现一个接受IronPython.Runtime.Slice对象作为参数的的索引器就可以了。并且让这个索引器返回一个枚举类型对象。

当然,IronPython关于切片的操作并不仅仅是取出一个切片,它还可以通过切片修改序列。下面是Set函数的实现:

ContractedBlock.gif

ExpandedBlockStart.gifset方法的实现1

None.gifnamespaceTestDynamicLanguageConsole2ExpandedBlockStart.gif

ContractedBlock.gifdot.gif{3InBlock.gifpublicclassDLRFriendlyCollection : Collection, INotifyPropertyChanged4ExpandedSubBlockStart.gif

ContractedSubBlock.gifdot.gif{5InBlock.gifpublicIEnumerablethis[Slice slice]6ExpandedSubBlockStart.gif

ContractedSubBlock.gifdot.gif{7InBlock.gifset8ExpandedSubBlockStart.gif

ContractedSubBlock.gifdot.gif{9InBlock.gifif(slice==null)10ExpandedSubBlockStart.gif

ContractedSubBlock.gifdot.gif{11InBlock.gifthrownewArgumentNullException("slice");12ExpandedSubBlockEnd.gif }13InBlock.gif14InBlock.gifintstart;15InBlock.gifintstop;16InBlock.gifintstep;17InBlock.gif slice.indices(this.Count,outstart,outstop,outstep);18InBlock.gif19InBlock.gifif(step==1)20ExpandedSubBlockStart.gif

ContractedSubBlock.gifdot.gif{21InBlock.gif//22InBlock.gif//这里的策略是不一样的。如果Step为1,value的长度可以和Slice的长度不一样。23InBlock.gif//这时,会将value插入到Start的位置,并将原先Start到stop之间的节点删除。24InBlock.gif//25InBlock.gif26InBlock.gif//Remove old data27InBlock.giffor(inti=start; i

ContractedSubBlock.gifdot.gif{29InBlock.gifthis.RemoveAt(start);30ExpandedSubBlockEnd.gif }31InBlock.gif32InBlock.gif//Insert new data33InBlock.gifforeach(stringiteminvalue)34ExpandedSubBlockStart.gif

ContractedSubBlock.gifdot.gif{35InBlock.gifthis.Insert(start, item);36InBlock.gif start++;37ExpandedSubBlockEnd.gif }38ExpandedSubBlockEnd.gif }39InBlock.gifelse40ExpandedSubBlockStart.gif

ContractedSubBlock.gifdot.gif{41InBlock.gif//42InBlock.gif//但是,如果Step不为1,value的长度就需要和Slice的长度完全匹配。会使用Value43InBlock.gif//中的每一项替换Slice命中的项目。44InBlock.gif//45InBlock.gif46InBlock.gif//检查一下数量对不对47InBlock.gifintcount=step>0?(stop-start+step-1)/step48InBlock.gif : (stop-start+step+1)/step;49InBlock.gif Listlist=newList(value.Cast());50InBlock.gifif(list.Count!=count)51ExpandedSubBlockStart.gif

ContractedSubBlock.gifdot.gif{52InBlock.gifthrownewArgumentException("size is not same.");53ExpandedSubBlockEnd.gif }54InBlock.gif55InBlock.gif//一个一个设置56InBlock.gifIEnumerator enumerator=value.GetEnumerator();57InBlock.gif enumerator.MoveNext();58InBlock.gifif(step>0)59ExpandedSubBlockStart.gif

ContractedSubBlock.gifdot.gif{60InBlock.giffor(inti=start; i

ContractedSubBlock.gifdot.gif{62InBlock.gifthis[start]=enumerator.Currentasstring;63InBlock.gif start+=step;64InBlock.gif enumerator.MoveNext();65ExpandedSubBlockEnd.gif }66ExpandedSubBlockEnd.gif }67InBlock.gifelse68ExpandedSubBlockStart.gif

ContractedSubBlock.gifdot.gif{69InBlock.giffor(inti=start; i>stop; i+=step)70ExpandedSubBlockStart.gif

ContractedSubBlock.gifdot.gif{71InBlock.gifthis[start]=enumerator.Currentasstring;72InBlock.gif start+=step;73InBlock.gif enumerator.MoveNext();74ExpandedSubBlockEnd.gif }75ExpandedSubBlockEnd.gif }76ExpandedSubBlockEnd.gif }77ExpandedSubBlockEnd.gif }78ExpandedSubBlockEnd.gif }79ExpandedSubBlockEnd.gif }80ExpandedBlockEnd.gif}

Python对切片的操作逻辑很复杂,这里就不详细的讨论了。我在上面的代码中加入了一些注释。相信可以帮助你理解实现。

最后,还有删除的操作:

ContractedBlock.gif

ExpandedBlockStart.gif删除方法的实现1

None.gifnamespaceTestDynamicLanguageConsole2ExpandedBlockStart.gif

ContractedBlock.gifdot.gif{3InBlock.gifpublicclassDLRFriendlyCollection : Collection, INotifyPropertyChanged4ExpandedSubBlockStart.gif

ContractedSubBlock.gifdot.gif{5InBlock.gif//6InBlock.gif//我没有找到使用Slice对象作为参数的删除方法。因此只能通过实现下面两个7InBlock.gif//方法来让Collection支持切片删除动作。8InBlock.gif//9InBlock.gif//另外,需要注意的是,微软似乎没有在这里实现step默认值的策略。因此,你10InBlock.gif//必须同时实现下面两个函数才能够完整支持删除操作。11InBlock.gif//12InBlock.gifpublicvoid__delslice__(intstart,intstop)13ExpandedSubBlockStart.gif

ContractedSubBlock.gifdot.gif{14InBlock.gifintcount, step;15InBlock.gif PythonOps.FixSlice(this.Count, start, stop,null,outstart,outstop,outstep,outcount);16InBlock.gif17InBlock.giffor(inti=start; i

ContractedSubBlock.gifdot.gif{19InBlock.gifthis.RemoveAt(start);20InBlock.gif start+=step-1;21ExpandedSubBlockEnd.gif }22ExpandedSubBlockEnd.gif }23InBlock.gifpublicvoid__delslice__(intstart,intstop,intstep)24ExpandedSubBlockStart.gif

ContractedSubBlock.gifdot.gif{25InBlock.gifintcount;26InBlock.gif PythonOps.FixSlice(this.Count, start, stop, step,outstart,outstop,outstep,outcount);27InBlock.gif28InBlock.giffor(inti=start; i

ContractedSubBlock.gifdot.gif{30InBlock.gifthis.RemoveAt(start);31InBlock.gif start+=step-1;32ExpandedSubBlockEnd.gif }33ExpandedSubBlockEnd.gif }34ExpandedSubBlockEnd.gif }35ExpandedBlockEnd.gif}

注意,IronPython使用反射调用这些方法,因此不会有任何编译时检查,你需要自己确保这些方法的签名正确。(这一点,是我们这些静态类型语言er最不能理解的。)

最后再给一个IronPython中看起来酷酷的功能——列表解析

support_ironpython_slice_in_dotnet2.PNG

IronPython代码如下:

print[ iforiinTestCollectionifint(i)%2==0 ]

也就是说,返回一个符合if后面条件的项目的集合。呵呵,是不是有点像Linq的语法,幸运的是,支持上面的语法,我们不需要写任何代码。否则,我又要写一篇Blog了。

好的,我们已经实现了一些我认为对于IronPython很重要的一些语法的支持。IronPython还有很多重要的语法,可以帮助开发人员快速的实现业务逻辑,而不像C#那样,什么都还没有做,先自动生成外加手写了1000+代码。我对IronPython并不很熟悉。如果你对IronPython很熟悉,并且认为IronPython中还有其它的很重要的功能,而你又不知道如何让你的嵌入对象支持这个功能的。在下面留言。我会考虑再写一篇博客实现一下。

这些支持的代码确实已经很精简了(至少比扩展CPython容易多了,《Python核心编程》中关于扩展一节我都没能够深入的读下去,太多的类库和依赖了)。但是,实现这些功能,我们不得不修改现有类型的接口。有时候,这样的修改是难以完成的。比如:

有时候,我们不想在我们的业务逻辑对象上暴露这些类似"__delslice__"的方法。

有时候,我们不能在已有的系统已经定义的类型上添加这些方法。

有时候,对象的创建不由我们控制,因此我们也不能通过继承来增加这些方法。

下一节,我们将会不通过继承,在已有CLR类型上添加这些方法。每周一节,敬请关注

icon_sub_c1s16_d.gif

wink_smile.gif

大家可以从这里下载可运行的源代码。

免责声明:本系列文章,完全是我个人研读 IronPython源代 码后找到的实现方案,并未详细的研究过IronPython的相关官方文档。因此,并不保证符合微软DLR和IronPython的设计思路,亦不能保证 在DLR和IronPython 2.0正式发布后能够继续使用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值