作者:Doug Turnbull译者:豌豆花下猫@Python猫原文:https://softwaredoug.com/blog/2021/11/12/ruby-vs-python-for-loop.html
Ruby 与 Python 之间的差异在很大程度上可通过for
循环看出本质。
Python 拥有for
语句。对象告诉for
如何进行协作,而for
的循环体会处理对象返回的内容。
Ruby 则相反。在 Ruby 中,for
本身(通过each
)是对象的一个方法。调用者将for
循环体传递给这个方法。
在 Python 的语言习惯中,对象模型服从于 for 循环。而在 Ruby 中,for 循环服从于对象模型。
也就是说,在 Python 中,如果你想自定义迭代的过程,可以让对象告诉解释器该如何作迭代:
class Stuff:
def __init__(self):
self.a_list = [1,2,3,4]
self.position = 0
def __next__(self):
try:
value = self.a_list[self.position]
self.position += 1
return value
except IndexError:
self.position = 0
raise StopIteration
def __iter__(self):
return self
在这里,Stuff 使用 __next__ 和 __iter__ 魔术方法使自身可迭代(变为了可迭代对象)。
for data in Stuff():
print(data)
然而,在 Ruby 的用法中,你要做的恰恰相反。你要将 for 创建成一个方法,它接收代码(body 体)来运行。Ruby 将过程代码放在代码块中,这样它们就可以被用于传递。
然后,在each
方法中,使用yield
与代码块进行交互,将值传递给代码块来做你需要做的事情(对于任何方法,代码块都是一种隐式参数)。
如果我们重写上面的代码,会成这样:
class Stuff
def initialize
@a_list = [1, 2, 3, 4]
end
def each
for item in @a_list
yield item
end
end
end
使用each
进行迭代:
Stuff.new().each do |item|
puts item
end
不是将数据传给 for 循环(Python),而是将循环代码传给数据(Ruby)。
但区别还远不止于此:
Python 构建类似于 for 的结构,用于各种处理;Ruby 将数据处理工作放到方法中。
优秀的 Python 代码使用列表和字典解析式来实现map
和filter
,这些表达式的核心与 for/迭代的语义是相同的。
In [2]: [item for item in Stuff()]
Out[2]: [1, 2, 3, 4]
In [3]: [item for item in Stuff() if item % 2 == 0]
Out[3]: [2, 4]
Ruby 则继续使用方法优先的方式,除了each
方法,还有一系列常用于处理集合的新方法,如下所示:
class Stuff
...
def select
out = []
each do |e|
# If block returns truthy on e, append to out
if yield(e)
out