从Ruby学习Python;区别与相似之处
我对Ruby非常熟悉,现在觉得可能需要学习Python。对于那些同时懂这两种语言的人来说,能不能告诉我它们之间有哪些相似和不同的地方?
我希望能得到一个类似于我为JavaScript程序员学习Lua写的入门指南的列表:比如空格的重要性、循环的写法;Python中nil
的名称是什么,哪些值被认为是“真值”;使用类似map
和each
的写法是否常见,还是说大家更习惯用一些关于列表推导的东西?
如果我能收到各种各样的回答,我很乐意把它们整理成一个社区维基。如果没有,那大家可以互相争论,试着创建一个真正全面的列表。
编辑:为了更清楚,我的目标是使用“正确的”和符合习惯的Python写法。如果Python有类似inject
的功能,但没人用,因为有更好或不同的方法来遍历列表并在过程中累积结果,我想知道你们是怎么做的。也许我会更新这个问题,列出一些常见的目标,看看你们在Ruby中是怎么实现的,然后问问Python中对应的写法是什么。
5 个回答
我的建议是:不要去纠结两种语言的区别。你应该学会如何用Python来解决问题。就像每个问题都有一种Ruby的解决方式(这种方式在考虑到语言的局限性和优点时非常有效),Python也有它自己的解决方式。这两者是不同的。要充分利用每种语言,你真的应该学习这门语言本身,而不仅仅是从一种语言“翻译”到另一种语言。
不过,了解这些区别会帮助你更快适应,并能对Python程序进行一些临时的修改。这样开始写代码是没问题的。但你要尽量从其他项目中学习,理解架构和设计决策背后的原因,而不是仅仅了解语言语法的使用方法……
我和你一样,在学习Python的时候也在寻找inject
和其他函数式编程的方法。结果让我有点失望,因为并不是所有的功能都有,或者说Python更倾向于一种命令式的编程方式。不过,如果你仔细找的话,大部分的构造都是可以找到的。在某些情况下,有些库会让事情变得更简单。
这里有几个我觉得比较重要的点:
你在Ruby中知道的函数式编程模式在Python中也能找到,只是看起来稍微不同。例如,有一个叫做map的函数:
def f(x): return x + 1 map(f, [1, 2, 3]) # => [2, 3, 4]
同样,还有一个
reduce
函数可以用来处理列表等。不过,Python没有块(blocks),而且没有简化的语法来链接或组合函数。(如果想要一种不使用块的优雅方式,可以看看Haskell的丰富语法。)
出于某种原因,Python社区似乎更喜欢用命令式的方式来迭代,而不是像Ruby那样不改变状态来完成一些事情。例如,折叠(即
inject
)通常是用命令式的for
循环来实现,而不是用reduce
:running_total = 0 for n in [1, 2, 3]: running_total = running_total + n
这不仅仅是一种约定,Python的维护者也在强调这一点。例如,Python 3的发布说明明确表示更倾向于使用
for
循环而不是reduce
:如果你真的需要,使用
functools.reduce()
;不过99%的情况下,显式的for
循环更容易阅读。列表推导式是一种简洁的方式来表达复杂的函数操作(类似于Haskell的列表单子)。这些在Ruby中是没有的,可能在某些场景下会很有帮助。例如,找出字符串中所有回文的一个简单一行代码(假设你有一个
p()
函数可以判断回文)看起来是这样的:s = 'string-with-palindromes-like-abbalabba' l = len(s) [s[x:y] for x in range(l) for y in range(x,l+1) if p(s[x:y])]
在很多情况下,Python中的方法可以被当作无上下文的函数来使用,这一点你可能需要从Ruby中调整过来,但这可以非常强大。
如果这对你有帮助,我在2011年写过更多的想法:Python的“丑陋”。这些内容可能需要根据今天对机器学习的关注进行更新。
以下是我认为的一些主要区别:
Ruby有块(blocks),而Python没有。
Python有函数,而Ruby没有。在Python中,你可以把任何函数或方法传递给另一个函数。但在Ruby中,所有东西都是方法,而方法不能直接传递。你需要把它们包裹在Proc中才能传递。
Ruby和Python都支持闭包,但方式不同。在Python中,你可以在一个函数内部定义另一个函数。内部函数可以读取外部函数的变量,但不能修改它们。而在Ruby中,你使用块来定义闭包。闭包可以完全读取和修改外部作用域的变量。
Python有列表推导式,这种写法非常简洁。例如,如果你有一个数字列表,你可以写
[x*x for x in values if x > 15]
来得到一个新列表,包含所有大于15的值的平方。在Ruby中,你需要写:
values.select {|v| v > 15}.map {|v| v * v}
Ruby的代码看起来没有那么简洁。而且效率也不高,因为它首先把值数组转换成一个包含大于15的值的中间数组。然后,它再从中间数组生成一个最终数组,包含中间数组的平方。中间数组最后被丢弃。所以,在计算过程中,Ruby会在内存中有3个数组,而Python只需要输入列表和结果列表。
Python也提供了类似的映射推导式。
Python支持元组,而Ruby不支持。在Ruby中,你需要用数组来模拟元组。
Ruby支持switch/case语句,而Python不支持。
Ruby支持标准的expr ? val1 : val2
三元运算符,而Python不支持。Ruby只支持单继承。如果你需要模拟多重继承,可以定义模块并使用混入(mix-ins)把模块方法引入类中。而Python支持多重继承,而不是模块混入。
Python只支持单行的lambda函数。Ruby的块有点像lambda函数,但可以很大。因此,Ruby的代码通常写得比Python更具函数式风格。例如,在Ruby中遍历一个列表,通常会这样写:
collection.each do |value| ... end
这个块的工作方式很像一个传递给
collection.each
的函数。如果你在Python中做同样的事情,你需要定义一个命名的内部函数,然后把它传递给集合的每个方法(如果列表支持这个方法):def some_operation(value): ... collection.each(some_operation)
这样写起来就不太顺畅了。所以,通常在Python中会使用以下非函数式的方法:
for value in collection: ...
在两种语言中安全使用资源的方式有很大不同。这里的问题是你想分配一些资源(打开文件、获取数据库游标等),对其执行某些操作,然后即使发生异常也能安全地关闭它。
在Ruby中,由于块的使用非常简单(见第9点),你通常会把这个模式写成一个方法,接受一个块作为对资源执行的操作。
在Python中,传入一个函数来执行操作就显得有些笨重,因为你需要写一个命名的内部函数(见第9点)。相反,Python使用
with
语句来安全处理资源。有关更多细节,请参见 如何正确清理Python对象?。