Python的列表理解与.NET LINQ

pro*_*eek 54 c# python linq list-comprehension

以下简单的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();
Run Code Online (Sandbox Code Playgroud)

可以使用列表推导将其翻译成python,如下所示.

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

小智 58

(警告:猛犸象回答.直到第一条水平线的部分是一个很好的tl; dr部分,我想)

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

首先:Afaik,LINQ查询是懒惰地执行 - 如果是这样的话,生成器表达式是一个更接近的Python概念(无论是方式,列表,字典和集合理解在概念上只是生成器表达式提供给list/dict/set构造函数! ).

此外,还有一个概念上的区别:正如名称所示,LINQ用于查询数据结构.List-/dict-/set comprehensions可以应用于此(例如,过滤和投影列表的项目).所以它们实际上不那么通用(正如我们将要看到的,LINQ中内置的很多东西都不是内置的).同样,生成器表达式是一种在某种程度上形成一次性前向迭代器的方法(我喜欢将它视为生成器函数的lambda,只有没有丑陋的long关键字;))而不是描述复杂查询的方法.它们重叠,是的,但它们并不相同.如果你想在Python中使用LINQ的所有功能,你将不得不编写一个完整的生成器.或者结合内置和内置的众多强大的发电机itertools.


现在,用于LINQ功能的Python同行Jon Skeet命名为:

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

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

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

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

请注意,对于每个x_item,这不会遍历整个y,它只会获得第一个匹配.

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

这更难.Python没有匿名类型,但如果你不介意弄乱它们,它们很容易做到__dict__:

class Anonymous(object):
    def __init__(self, **kwargs):
        self.__dict__ = kwargs
Run Code Online (Sandbox Code Playgroud)

然后,我们可以(Anonymous(x=x, y=y) for ...)获取具有相应值的对象列表xy成员列表.正确的做法通常是将结果提供给approriate类的构造函数,比如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
Run Code Online (Sandbox Code Playgroud)

例:

>>> 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: [...], ...})
Run Code Online (Sandbox Code Playgroud)

但是,这要求我们分组的任何东西都是可以清洗的.有可能避免这种情况,如果有公共需求,我会做出准备.但就目前而言,我很懒惰:)

我们也可以只返回组的迭代没有我们通过分组的值,通过调用.values()的结果(当然,我们可以养活list得到的东西,我们可以索引并重复几次).但谁知道我们是否不需要团体价值观......

  • 订购(按x.foo升序排序,y.bar降序)

排序需要特殊的语法?内置也sorted适用于iterables:sorted(x % 2 for x in range(10))sorted(x for x in xs, key=attrgetter('foo')).默认情况下按升序排序,关键字参数reverse按降序排列.

唉,通过多个属性进行分类并不容易,特别是在混合上升和下降时.嗯...食谱的主题?

  • 中间变量(让tmp = x.foo)

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

(x * 2 for x in iterable)
Run Code Online (Sandbox Code Playgroud)

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

def doubles(iterable):
    for x in iterable:
        times2 = x * 2
        yield times2
Run Code Online (Sandbox Code Playgroud)

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


请注意,尽管LINQ to Objects处理委托,但其他查询提供程序(例如LINQ to SQL)可以处理描述查询的表达式树,而不仅仅是呈现可执行委托.这允许将查询转换为SQL(或其他查询语言) - 再次,我不知道Python是否支持这种事情.但它是LINQ的重要组成部分.

Python绝对没有这样的东西.列表表达式一对一地对应于(可能嵌套的)for循环中的普通列表,生成器表达式与发生器一一对应.给定parserast模块,理论上可以编写用于将理解转换为例如SQL查询的库.但没有人关心.

  • 要使其中一些更"Pythonic"(即本机/惯用语),请查看Python的`collections.namedtuple`和`itertools.groupby` (2认同)
  • 扁平化:`itertools.chain` (2认同)

Jon*_*eet 24

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

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

如VB做的,但这里是它的C#不支持尽可能多的查询表达式中支持:

  • 预测(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 Objects处理委托,但其他查询提供程序(例如LINQ to SQL)可以处理描述查询的表达式树,而不仅仅是呈现可执行委托.这允许将查询转换为SQL(或其他查询语言) - 再次,我不知道Python是否支持这种事情.但它是LINQ的重要组成部分.


Rob*_*ire 17

通过使用asq Python包,您可以轻松地在Python中完成大多数事情,您可以使用LINQ-for-objects在C#中完成.使用asq,您的Python示例变为:

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