• IronPython and LINQ to Objects (I): 语言特性


    Visual Studio的开发者Harry Pierson在DevHawk上发布了一组文章,讨论了如何在IronPython中使用LINQ to XML。受他的启发,我也对如何在IronPython中使用LINQ to Objects进行了研究。从本篇开始,我将逐步介绍LINQ在IronPyton中的使用方法。

    《LINQ in Action》指出,LINQ由两个互为补充的部分组成:一组处理数据的工具和一组对编程语言的扩展。LINQ为C#引入了很多新特性,它们构成了语言集成查询(Language-INtegrated Query)的基础。本文将探讨IronPython是否提供了相似的特性,使得IronPython也具备接纳LINQ的能力。其中,一些C#的例子来自于《LINQ in Action》。

    1. 隐式类型局部变量(Implicitly typed local variables

      var i = 5;

    静态强类型语言的发展趋势之一是不断增强编译期的自动代码生成。编译器会生成大量的只有编译器才知道名字的(或者名字非常复杂的)“匿名类型”。为了让程序员可以操纵这些类型的对象,C#语言引入了关键字var,通过类型推演技术自动获得对象的类型。在保证类型安全的前提下,简化了代码,提高了程序的可理解性。类似地,C++复用了关键字auto,用于自动地声明对象的类型。

    Python是动态强类型语言,不要求程序员声明变量的类型。如果在运行时发生类型错误,Python运行时会抛出异常。因此,与C#对应的Python代码非常简单。

      i = 5

    2. 对象和集合的初始化器(Object and collection initializers

    new Point {X = 1, Y = 2};

    var processes = new List<ProcessData> {
      new ProcessData {Id=123,},
      new ProcessData {Id=456,}
    } ;

     

    C#的对象初始化器可以在一条语句中为对象指定一个或多个字段的值。C#的集合初始化器可以在一条语句中初始化集合中的所有的对象。这种声明式的初始化代码,使得对象创建变得更加轻松。

    IronPython开发团队很好地将Python与CLR集成在一起。对于用C#编写的类型,IronPython可以用如下语句初始化。

      Point(X = 1, Y = 2)

    而Python内建的list、tuple等集合类型也支持声明式的初始化。

    processes = [ProcessData(Id = 123,),
      ProcessData(Id=456,)]

    由于不需要关键字new,IronPython代码较C#代码更简单。实际上,C#是不需要关键字new的。在C++中,关键字new表示对象的内存从堆上分配;程序员可以重载new提供自定义的内存分配方式。在C#中,对象的内存分配方式取决于它是引用类型还是结构类型,而且C#也不允许程序员控制内存分配。因此,其关键字new可以被视为语法“噪音”。

     

    3. Lambda表达式(Lambda expressions

      address => address.City == "Paris"

    在引入lambda表达式之后,C#具备了一定的函数式语言的风格:允许程序员将运算逻辑抽象为对象,在代码中构造、传递和调用。虽然delegate和匿名函数也提供一定的函数式风格的支持,但是lambda表达式将C#的表达能力推向了新的高度。

    Python也支持lambda表达式,对应的Python代码如下。

      lambda address: address.City == "Paris"

     

    4. 扩展方法(Extension methods

      static void Dump(this object o);

    扩展方法以非入侵的方式来扩展已有类型,允许程序员方便地添加领域相关的辅助方法。这一思路与Herb Sutter在C++ Coding Standard中提倡的non-virtual and non-friend function相似。在编译时,C#编译器会自动绑定扩展方法。这样,程序员就可以以obj.Dump()的形式来调用扩展方法。

    Python语言不支持扩展方法。但是,利用Python语言在运行期修改对象成员的能力,我们仍旧可以方便地扩展对象。

      1: def bind1st(func, obj):

      2:     def binder(*__args, **__kws):

      3:         return func(obj, *__args, **__kws)

      4:     return binder

      5:    

      6: class DataObject(object): pass

      7:

      8: d = DataObject()

      9: d.x = 1

     10: d.Add = bind1st(lambda self, y: self.x + y, d)

     11: d.Add(2) # result is 3

     

    以上代码展示了一种在运行期扩展对象的可能策略。

    • 第1~4行的函数bind1st的输入是函子func和对象obj,输出是一个新函子。该新函子调用func,且func第一个参数被绑定为obj。
    • 第6行声明了类DataObject,它没有任何成员。
    • 第8行定义了变量d,它的类型是DataObject。此时,d没有任何成员。
    • 第9行为变量d增加了成员对象x。
    • 第10行为变量d增加了成员函数Add。
    • 第11行调用变量d的成员函数Add,其返回值是3。

    在Python语言中,有多种方法来扩展对象的成员函数。在本系列文章中,我将展示另一种“扩展方法”的实现,并应用在对LINQ to Objects的调用中。

     

    5. 匿名类型(Anonymous types

      var contact = new {Name = "Bob", Age = 8}

    匿名类型允许程序员方便地构造匿名数据对象。这在Python中也是非常容易的任务。

      1: def makeobj(**kws):

      2:     class Object:

      3:         def __init__(self, kws):

      4:             self.__dict__.update(kws)

      5:     return Object(kws)

      6:

      7: contact = makeobj(Name = "Bob", Age = 0)

      8: print contact.Name # output is "Bob"

      9: print contact.Age  # output is "0"

    • 第1~5行的函数makeobj是Python Cookbook提供的Python惯用法。
    • 第1行声明了函数makeobj。它利用Python语言特性,将其参数序列转换为字典对象kw。
    • 第2~4行定义了函数makeobj的内部类Object。Object的初始化函数__init__用字典对象kws初始化对象自身。字典的键成为其数据成员的名字,字典的值成为其数据成员的值。
    • 第5行返回一个用kws初始化的Object对象。 
    • 第7~8行的代码展示了makeobj的调用方法和所得对象的成员。

    可见,利用makeobj可以用简短的代码构造出任意成员的对象。这些对象配合动态语言Duck Typing特性,优雅地简化了代码。

    综上所述,IronPython可以模拟出LINQ为C#引入的新特性。这为IronPython平滑地调用LINQ to Objects奠定了基础。

  • 相关阅读:
    GitHub注册和Git安装
    Git克隆与更新代码
    三,jenkins配置构建执行状态
    四,jenkins设置定时任务
    二,jenkins创建构建任务
    一,jenkins环境搭建
    Python Selenium Web自动化上传/下载文件图文详解
    Robot Framework自动化测试(七)--- jybot模式
    jmeter eval函数之妙用(参数化文件内含各种表达式)
    jmeter ssh+jdbc用法
  • 原文地址:https://www.cnblogs.com/liangshi/p/1726368.html
Copyright © 2020-2023  润新知