Python的列表理解与.NET LINQ

以下简单的LINQ代码

string[] words = { "hello", "wonderful", "linq", "beautiful", "world" }; // Get only short words var shortWords = from word in words where word.Length <= 5 select word; // Print each word out shortWords.Dump(); 

可以使用列表理解翻译成python如下。

 words = ["hello", "wonderful", "linq", "beautiful", "world"] shortWords = [x for x in words if len(x) <=5] print shortWords 
  • LINQ只是实现列表理解的另一个想法吗?
  • LINQ可以做什么样的例子,但列表理解是不能做的。

(警告:猛犸象的答案在前面,直到第一条水平线的部分是好的,我想是dr部分)

我不确定我是否有资格成为Python大师……但是我对Python中的迭代有一个很好的把握,所以让我们试试:)

首先:Afaik,LINQ查询是懒惰地执行的 – 如果是这种情况,生成器expression式是一个更接近Python的概念(无论哪种方式,列表,字典和集合的概念只是在列表/字典/集合构造函数中产生的expression式! )。

另外,还有一个概念上的区别:LINQ正如名称所说,查询数据结构。 List- / dict- / set comprehensions是可能的应用(例如过滤和投影列表的项目)。 所以他们实际上不那么普遍(正如我们将会看到的,LINQ中内置的许多东西都不是内置的)。 同样,生成器expression式是一种用来形成一次性前向迭代器的方式(我喜欢把它看作是生成器函数的lambda,只有没有丑陋的长关键字)),而不是一种描述复杂查询的方法。 他们重叠,是的,但他们不完全相同。 如果你想在Python中使用LINQ的所有function,你将不得不编写一个完整的生成器。 或者结合内置和itertools的众多强大的发生器。


现在,Python对应的LINQfunctionJon Skeet命名为:

预测: (x.foo for ...)

过滤: (... if x.bar > 5)

  • 联合(x联合y在x.foo等于y.bar)

最接近的是((x_item, next(y_item for y_item in y if x_item.foo == y_item.bar)) for x_item in x) ,我想。

请注意,这不会遍历整个y每个x_item,它只会得到第一个匹配。

  • 组连接(x在x.foo上连接y等于y.bar为g)

这很难。 Python没有匿名types,尽pipe如果你不介意与__dict__混淆的话,它们是__dict__

 class Anonymous(object): def __init__(self, **kwargs): self.__dict__ = kwargs 

然后,我们可以做(Anonymous(x=x, y=y) for ...)来获得具有相应值的xy成员的对象列表。 正确的事情通常是将结果提供给一个合适的类的构造函数,比如XY。

  • 分组(由x.bar组成的x.foo)

现在它变得毛茸茸的…没有固定的方式,afaik。 但是如果我们需要的话,我们可以自己定义它:

 from collections import defaultdict def group_by(iterable, group_func): groups = defaultdict(list) for item in iterable: groups[group_func(item)].append(item) return groups 

例:

 >>> from operator import attrgetter >>> group_by((x.foo for x in ...), attrgetter('bar')) defaultdict(<class 'list'>, {some_value_of_bar: [x.foo of all x where x.bar == some_value_of_bar], some_other_value_of_bar: [...], ...}) 

尽pipe如此,这就要求我们分组的任何东西都是可排除的。 这是可以避免的,如果有公众需求,我会做出一个刺。 但现在,我正在懒惰:)

我们也可以通过在结果上调用.values()来返回一个没有我们分组的值的迭代组(当然,我们可以提供这个 list来获得我们可以索引和迭代几次的东西)。 但谁知道我们是否需要团体价值?

  • sorting(orderby x.foo升序,y.bar降序)

sorting需要特殊的语法? 内置的sorted工作的iterables也: sorted(x % 2 for x in range(10)) sorted(x for x in xs, key=attrgetter('foo')) sorted(x % 2 for x in range(10))sorted(x for x in xs, key=attrgetter('foo')) 。 按照默认的升序sorting,关键字参数reverse按降序排列。

唉,通过多个属性进行sorting并不是那么容易,尤其是在混合升序和降序时。 嗯…配方的主题?

  • 中间variables(让tmp = x.foo)

不,在理解或生成器expression式中是不可能的,正如名字所说,它们应该是expression式(通常只有一行或两行)。 但是,在发电机function中完全可能:

 (x * 2 for x in iterable) 

重写为具有中间variables的生成器:

 def doubles(iterable): for x in iterable: times2 = x * 2 yield times2 

展平: (c for s in ("aa","bb") for c in s )


请注意,虽然LINQ to Objects处理委托,但是其他查询提供者(例如LINQ to SQL)可以处理描述查询的expression式树,而不是只呈现可执行的委托。 这允许将查询翻译成SQL(或其他查询语言) – 再一次,我不知道Python是否支持这种事情。 这是LINQ的重要组成部分。

Python绝对没有这样的事情。 列表expression式一对一地在一个(可能嵌套的)for循环中累积一个普通列表,生成器expression式与一个生成器一一对应。 给定parserast模块, 理论上可以编写一个库来将理解转换为例如SQL查询。 但是没有人关心。

那么,你需要区分一些不同的东西:

  • LINQ标准查询运算符
  • 在C#中的LINQ查询expression式
  • VB中的LINQ查询expression式

C#不像VB那样支持查询expression式,但是它支持的是:

  • 投影( select x.foo
  • 过滤( where x.bar > 5
  • x join y on x.foo equals y.barx join y on x.foo equals y.bar
  • 组连接( x join y on x.foo equals y.bar into g
  • 分组( group x.foo by x.bar
  • sorting( orderby x.foo ascending, y.bar descending
  • 中间variables( let tmp = x.foo
  • 展平( from x in y from z in x

我不知道有多less人直接支持Python的列表parsing。

请注意,虽然LINQ to Objects处理委托,但是其他查询提供者(例如LINQ to SQL)可以处理描述查询的expression式树 ,而不是只呈现可执行的委托。 这允许将查询翻译成SQL(或其他查询语言) – 再一次,我不知道Python是否支持这种事情。 这是LINQ的重要组成部分。

通过使用asq Python包,您可以轻松地完成大部分Python中的工作,您可以在C#中使用LINQ for …对象进行操作。 使用asq,你的Python例子变成:

 from asq.initiators import query words = ["hello", "wonderful", "linq", "beautiful", "world"] shortWords = query(words).where(lambda x: len(x) <= 5) 

我不是Python大师,但我会说,Python实际上支持所有这些,因为你可以嵌套列表parsing,并包括所有你想要的lambdaexpression式。 (如果列表理解过于复杂,往往难以理解,但是,不包括“特定语法”来完成这一切。

大多数function可以使用以下方式进行复制: – 列表parsing生成器 – lambda函数或内置函数(如filter()map() )或itertools模块中的函数

例如,如果您想复制以下行为:

  • 预测 :这将是列表理解的左边部分…可以是单值,也可以是元组。 例如: [ (k,v) for k,v in my_dict.items() if k.startswith("abc"] 。也可以使用map()
  • 过滤 :这将是在if之后的右侧expression式。 你也可以使用filter()
  • sorting :只需使用内置的sorted()
  • 分组汇总 :使用内buildmin()max()itertools.groupby()

关于join扁平化 ,我认为你将不得不“手动”…

(总是有好的Python达到快速参考 )