(警告:下面有一篇庞大的回答。我想,前面到第一个水平线的部分可以作为一个很好的 tl;dr 部分)
我不确定自己是否有资格成为 Python 大师……但我对 Python 中的迭代有着扎实的掌握,所以让我们来试试吧 :)
首先:据我所知,LINQ 查询是惰性执行的——如果是这样的话,生成器表达式就是更接近 Python 概念的东西了(无论如何,列表、字典和集合推导式在概念上只是被馈送到列表/字典/集合构造函数的生成器表达式!)。
此外,还存在一个概念上的区别:LINQ 用于查询数据结构,正如其名称所示。列表/字典/集合推导式是这个概念的可能应用(例如,过滤和投影列表项)。因此,它们实际上不太通用(正如我们将看到的,许多内置于 LINQ 中的东西并没有内置于它们中)。同样,生成器表达式是一种在原地形成一次性前向迭代器的方式(我喜欢把它看作是生成器函数的 lambda,只是没有一个丑陋而冗长的关键字 ;)),而不是描述复杂查询的方式。它们有重叠,是的,但它们并不相同。如果你想在 Python 中拥有 LINQ 的全部功能,你将不得不编写一个完整的生成器。或者结合内置的众多强大的生成器和 itertools
。
现在,Python的LINQ功能对应如下:
投射: (x.foo for ...)
过滤: (... if x.bar > 5)
- 连接(x join y on x.foo equals 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 join y on x.foo equals y.bar into g)
这更难。 Python没有匿名类型,虽然如果您不介意使用__dict__
,则可以轻松完成:
class Anonymous(object):
def __init__(self, **kwargs):
self.__dict__ = kwargs
然后,我们可以执行 (Anonymous(x=x, y=y) for ...)
以获取一个对象列表,这些对象具有相应值的 x
和 y
成员。
通常正确的做法是将结果提供给适当类的构造函数,比如 XY。
现在变得复杂了... 据我所知,没有内置的方法。但如果需要,我们可以自己定义它:
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: [...], ...})
这需要我们分组的任何内容都是可哈希的。虽然可以避免这种情况,但如果公众需要,我会尝试一下。但目前,我有点懒 :)
我们还可以通过在结果上调用.values()
来返回一个没有我们分组的值的可迭代组,当然,我们可以将它提供给list
,以获取可以索引并多次迭代的内容。但谁知道我们是否需要组值...
- 排序(按 x.foo 升序排列,y.bar降序排列)
排序需要特殊的语法吗?内置的sorted
也适用于可迭代对象:sorted(x % 2 for x in range(10))
或sorted(x for x in xs, key=attrgetter('foo'))
。默认升序排序,关键字参数reverse
按降序排序。
不幸的是,据我所知,按多个属性进行排序并不容易,特别是当混合升序和降序时。嗯...这是一篇食谱的主题吗?
不,列表推导式或生成器表达式中不可能实现这个功能 - 正如名称所示,它们应该是表达式(通常只跨越一两行)。但在生成器函数中完全可以实现:
(x * 2 for x in iterable)
使用中间变量重写为生成器:
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)可以处理表达式树,这些表达式树描述查询而不仅仅是呈现可执行的委托。这使得查询可以被翻译成SQL(或其他查询语言)-同样,我不知道Python是否支持这种功能。但这是LINQ的一个重要部分。
Python绝对不会做这样的事情。列表表达式与在(可能嵌套的)for循环中累积普通列表一一对应,生成器表达式与生成器一一对应。
鉴于解析器和ast模块,理论上可以编写一个库,将推导转换为例如SQL查询。但没有人关心这个。