关于c#:Python的列表理解与.NET LINQ

Python's list comprehension vs .NET LINQ

以下简单的LINQ代码

1
2
3
4
5
6
7
8
9
10
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。

1
2
3
words = ["hello","wonderful","linq","beautiful","world"]
shortWords = [x for x in words if len(x) <=5]
print shortWords

  • Linq是实现列表理解的另一个方法吗?
  • LINQ可以做但列表理解不能做的例子是什么?


(警告:前面有一个庞大的答案。到第一条水平线的部分是很好的tl;我想是dr section)

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

首先:afaik、linq查询执行缓慢-如果是这样,生成器表达式是一个更接近python的概念(不管是哪种方式,list-、dict-和set理解概念上都只是生成器表达式被送入list/dict/set构造函数!)。

另外,还有一个概念上的区别:正如名称所说,Linq用于查询数据结构。list-/dict-/set理解可以应用于此(例如,筛选和投影列表中的项)。因此,它们实际上不那么一般(正如我们将看到的,许多内置在LINQ中的东西并不是内置在它们中的)。同样,生成器表达式是就地构造一次性前向迭代器的一种方法(我喜欢将其视为生成器函数的lambda,只不过没有难看的长关键字;)而不是描述复杂查询的方法。它们重叠,是的,但它们不相同。如果您想在Python中使用Linq的所有功能,就必须编写一个完全成熟的生成器。或者将众多内置和内置的强大发电机结合在一起。

现在,python对应的linq功能jon skeet命名为:

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

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

  • Joins (x join y on x.foo equals y.bar)

我想最接近的东西应该是1号文件(3号文件)。

注意,对于每个x_项,这不会在整个y上迭代,只会得到第一个匹配项。

  • Group joins (x join y on x.foo equals y.bar into g)

这更难。python没有匿名类型,但是如果您不介意干扰__dict__,这些类型对您自己来说是微不足道的:

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

然后,我们可以通过(Anonymous(x=x, y=y) for ...)来得到一个对象列表,这些对象具有具有各自值的xy成员。正确的做法通常是将结果提供给适当类(比如xy)的构造函数。

  • Grouping (group x.foo by x.bar)

现在它变得多毛了…阿法克,没有建造的方法。但如果我们需要,我们可以自己定义它:

1
2
3
4
5
6
7
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

例子:

1
2
3
>>> 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: [...], ...})

不过,这需要我们分组的任何内容都是可散列的。这是可以避免的,如果有公共需求的话,我会试试的。但现在,我懒惰了:)

我们也可以通过对结果调用.values()(当然,我们可以将结果反馈给list以获得一些我们可以索引和迭代多次的东西),返回一个不带我们分组的值的组的iterable。但谁知道我们是否不需要群体价值观…

  • Ordering (orderby x.foo ascending, y.bar descending)

排序需要特殊的语法?嵌入的sorted也适用于iterables:sorted(x % 2 for x in range(10))sorted(x for x in xs, key=attrgetter('foo'))。按默认升序排序,关键字参数reverse给出降序。

遗憾的是,通过多个属性进行Afaik排序并不是那么容易,尤其是在混合使用升序和降序时。隐马尔可夫模型。。。食谱主题?

  • Intermediate variables (let tmp = x.foo)

不,在理解或生成器表达式中是不可能的——正如名称所说,它们应该是表达式(通常只跨一行或两行)。不过,在发电机功能方面是完全可能的:

1
(x * 2 for x in iterable)

用中间变量重写为生成器:

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

压扁:(c for s in ("aa","bb") for c in s )

Note that although LINQ to Objects deals with delegates, other query providers (e.g. LINQ to SQL) can deal in expression trees which describe the query instead of just presenting executable delegates. This allows the query to be translated into SQL (or other query languages) - again, I don't know whether Python supports that sort of thing or not. It's a significant part of LINQ though.

Python绝对不会这样做。列表表达式与在for循环(可能是嵌套的)中累积普通列表一一对应,生成器表达式与生成器一一对应。考虑到parserast模块,理论上可以编写一个库来将理解转换为SQL查询。但没人在乎。


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

  • LINQ标准查询运算符
  • C中的LINQ查询表达式#
  • VB中的LINQ查询表达式

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

  • 预测(select x.foo)
  • 过滤(where x.bar > 5)
  • 连接(x 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)
  • 订货(orderby x.foo ascending, y.bar descending)
  • 中间变量(let tmp = x.foo)
  • 压扁(from x in y from z in x)

我不知道在python的列表理解中有多少是直接支持的。

注意,尽管linq to对象处理委托,但其他查询提供程序(例如linq to sql)可以处理描述查询的表达式树,而不只是表示可执行委托。这允许将查询转换为SQL(或其他查询语言)——同样,我不知道Python是否支持这种类型的东西。但它是Linq的重要组成部分。


通过使用asq python包,您可以很容易地在python中完成大多数可以在c中使用linq处理对象的事情。使用asq,您的python示例将变为:

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


我不是Python专家,但我想说的是,Python实际上支持所有这些表达式,因为您可以嵌套列表理解,并包含您想要的所有lambda表达式。(如果列表理解变得太复杂,那么它们往往很难阅读,尽管…),但是不,它不包括完成所有这些的"特定语法"。

大部分功能可通过以下方式重现:-列出理解或生成器-lambda函数或内置函数(如filter()map()或来自itertools模块的函数)

例如,如果要复制以下行为:

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

关于连接或展平,我认为你必须"用手做"…

(总是很好地让python在到达时快速参考)