我们可以在列表上使用链方法吗?

问题描述 投票:0回答:3

我来自Ruby,你可以非常轻松地进行方法链。让我们看一个例子。如果我想从列表中选择所有偶数并向其添加 5。我会在 Ruby 中做类似的事情。

nums = [...]
nums.select {|x| x % 2 == 0 }.map { |x| x + 5 }

在Python中变成

nums = [...]
list(map(lambda x: x + 5, filter(lambda x: x % 2 == 0, nums)))

Python 语法看起来很糟糕。我尝试谷歌并没有找到任何好的答案。我所看到的只是如何使用自定义对象实现类似的效果,但没有任何方法可以以这种方式处理列表。我是不是错过了什么?

在调试控制台中时,在数组中获取一些 ActiveRecord 对象曾经非常有帮助,我可以直接链接方法来处理实体以进行调试。对于 Python,这似乎工作量太大了。

python ruby method-chaining
3个回答
6
投票

在 Ruby 中,每个可枚举对象都包含

Enumerable
接口,这就是为什么我们得到像你提到的所有这些有用的方法。但在 Python 中,可迭代对象没有通用的超类。可迭代的字面意思是“支持
__iter__
的事物”,虽然有一个名为 Iterable
抽象类,它假装是所有可迭代的超类,但它实际上不提供任何方法,并且不提供任何方法。不位于所有可迭代的继承链中(它使用 dunder 方法的魔力覆盖
isinstance
issubclass
的行为,就像您可以通过编写
+
来覆盖
__add__
一样)。

Alakazam 库正是实现了这个功能。 (披露:我是这个库的创建者和维护者,但它完全符合你的要求,所以我会在这里提到它)

Alakazam 提供了

Alakazam

 类,它包装了任何 Python 可迭代对象,并提供所有内置 Python 序列方法、所有 
itertools
 模块以及其他一些有用的面向流的方法(这些方法不是默认情况下包含在 Python 中。考虑上面的例子

nums.select {|x| x % 2 == 0 }.map { |x| x + 5 }
在 Python 中,看起来像

list(map(lambda x: x + 5, filter(lambda x: x % 2 == 0, nums)))
使用 Alakazam,看起来就像

zz.of(nums).filter(lambda x: x % 2 == 0).map(lambda x: x + 5).list()
或者,使用 Alakazam 的 lambda 语法

zz.of(nums).filter(_1 % 2 == 0).map(_1 + 5).list()
只要合理,Alakazam 的方法(如 

filter

map
)就会懒惰地匹配 Python 的行为,因此我们仍然需要在末尾编写 
list()
 来消费可迭代对象并生成单个列表结果。


2
投票
正如评论中所述,这段 Ruby 代码:

nums = [...] nums.select {|x| x % 2 == 0 }.map { |x| x + 5 }

注意:为什么不使用#even?

nums = [...] nums.select {|x| x.even? }.map { |x| x + 5 }
甚至:

nums = [...] nums.select(&:even?).map { |x| x + 5 }
但是撇开挑剔不谈,这可以使用列表理解在 Python 中表达,这是非常干净的。

nums = [...] [x + 5 for x in nums if x % 2 == 0]
现在列表理解急切地生成一个完整的列表。想象一下像 

[1, 2, 3, 4, 5, 6, 7, 8]

 这样的原始列表。列表理解会给我们
[2, 4, 6, 8]
。数据集很简单。

但是想象一下

nums

list(range(100_000_000))
不是一个简单的数据集。即使我们只需要前四个值,将此列表理解应用于整个事情也会花费很多时间。

但是生成器表达式可以让我们惰性地生成我们需要的值。

from itertools import islice nums = range(100_000_000) evens_plus_five = (x + 5 for x in nums if x % 2 == 0) list(islice(evens_plus_five, 0, 5, 1))
正如评论中所建议的,在 Ruby 中使用 

#lazy

 和范围可以很容易地获得这种对大数据集的惰性求值优势。

nums = (1..100_000_000) nums.lazy.select(&:even?).map { |x| x + 5 }.take(5).to_a
如果您使用的是 Ruby 3,让我们让该块更加清晰。

nums = (1..100_000_000) nums.lazy.select(&:even?).map { _1 + 5 }.take(5).to_a
    

0
投票
我想提一下我编写的

streamable

模块(很想得到反馈!):

你的例子看起来像:

from streamable import Stream nums = range(10) # lazy operations pair_nums_plus_5: Stream[int] = ( Stream(lambda: nums) .filter(lambda n: n % 2 == 0) .map(lambda n: n + 5) ) print(list(pair_nums_plus_5))
[5, 7, 9, 11, 13]

    这是一个
  • 类型化模块,具有 100% 代码覆盖率且无依赖性
  • Stream[T]
    课程扩展
    Iterable[T]
    
    
  • 流是
  • 不可变的
  • 操作是
  • 惰性评估的,涵盖并发、分组/扁平化、异常捕获、速率限制、迭代进度日志记录……
最新问题
© www.soinside.com 2019 - 2024. All rights reserved.