我正在尝试对 Python 中的一组对象编写一个简单的查询,这在 C# 或 Ruby 中将是微不足道的和优雅的,但我很难在 Python 中使它优雅。我想我做错了什么。
在 C# 中:
list.Where(x => x.Foo > 10).Select(x => x.Bar).Where(x => x.Baz.StartsWith("/"))
这将创建一个枚举,包括为列表中的所有其他项目list[0].Bar
提供list[0].Foo
> 10 并以 等list[0].Bar.Baz
开头。'/'
数据从左到右清晰流动,右侧可以附加进一步的过滤/投影/聚合。
在红宝石中:
list.select { |x| x.foo > 10 }.map(&:bar).select { |x| x.baz.starts_with? '/' }
同样,从左到右的流程相当清晰,可以轻松附加进一步的操作。
但我在 Python 中的尝试似乎是倒退的,由内而外,而且通常很丑:
[x for x in (x.bar for x in (x for x in list if x.foo > 10)) if x.baz.startswith('/')]
现在我知道我可以在一个步骤中将地图和过滤器与列表理解结合起来,并且上面可以重写为:
[x.bar for x in list if x.foo > 10 and x.bar.baz.startswith('/')]
但这反而没有抓住重点。一方面,投影 x.bar 可能很昂贵,我不想对其进行两次评估;另一方面,投影和过滤只是我对流应用的两个潜在操作,我可以是排序、聚合、分页等,并不是所有的投影和过滤器都需要相邻,过滤器也不需要在投影之前应用而不是之后。
我是否试图将 Python 扭曲成它不是的东西?我通常会尽可能尝试以这种风格进行编程,无论是命令行(shell 管道)、C#、Ruby 还是 Java(比 Python 更痛苦)。我应该停止戳痛的地方吗?