从 Ruby 学习 Python;异同
Posted
技术标签:
【中文标题】从 Ruby 学习 Python;异同【英文标题】:Learning Python from Ruby; Differences and Similarities 【发布时间】:2011-06-13 17:32:08 【问题描述】:我非常了解鲁比。我相信我现在可能需要学习 Python。两者都知道的朋友,请问两者有什么概念相似,有什么不同?
我正在寻找一个类似于我为Learning Lua for javascripters 编写的入门书的列表:简单的东西,如空格重要性和循环结构; Python 中nil
的名称,以及哪些值被认为是“真实的”;使用 map
和 each
的等效项是惯用的,还是 mumble somethingaboutlistcomprehensions mumble 是规范?
如果我得到各种各样的答案,我很乐意将它们汇总到社区 wiki 中。否则,你们可以互相争吵,努力创建一份真正的综合清单。
编辑:明确地说,我的目标是“正确”和惯用的 Python。如果有一个 Python 相当于 inject
,但没有人使用它,因为有更好/不同的方法来实现迭代列表和累积结果的通用功能,我想知道你是怎么做的。也许我会用一系列共同目标来更新这个问题,你如何在 Ruby 中实现这些目标,并询问 Python 中的等价物是什么。
【问题讨论】:
我读到的唯一的东西是c2.com/cgi/wiki?PythonVsRuby,我真的不喜欢self和缩进,但我已经习惯了:) @SilentGhost 我强烈反对。我在问“语言之间有什么相同,有什么不同?”正如下面的许多答案所示,对此可能有非常明确和有用的答案。 @Phrogz:我明白了,所以这个问题无法回答。 @Phrongz - 为了回应我在您发布的元主题上所说的话,这个问题的问题是问题空间太大 - 对于一个问题来说这个话题太大了。两种语言之间存在数千种差异。 投票重新开放。那里的一些答案表明该问题在未来是可回答且有用的 【参考方案1】:以下是我认为的一些主要区别:
Ruby 有块; Python 没有。
Python 有函数;红宝石没有。在 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 支持 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
语句进行安全资源处理。详情请见How do I correctly clean up a Python object?。
【讨论】:
3. Python 3nonlocal
修复了这个 4。Python 还为您提供了生成器表达式(类似于列表推导,但在被要求之前不要计算任何东西 - 将列表推导视为馈送到 list
的生成器表达式(它接受一个可迭代并返回一个包含所有可迭代对象的列表)——这在某些情况下可以节省很多精力)。
7.是的,它确实。 val1 if expr else val2
。 8. 虽然我看到它主要用于 mixin 风格的增强。
@ClintMiller 哇,没有开关/外壳?那么,在 Python 中实现类似功能的建议方法是什么?如果/否则/如果?
您在#4 中的 ruby 示例不是惯用的。写values.map|v| v*v if v > 15.compact
会更红宝石(和可读)。恕我直言,这比您的 python 示例更具表现力(当然也更清晰)。
除上述之外,使用 !紧凑函数的版本避免了数组的副本:values.map|v| v*v if v > 15.compact!
。这意味着只有输入列表和结果列表存在于内存中。请参阅此处的#4:igvita.com/2008/07/08/6-optimization-tips-for-ruby-mri【参考方案2】:
我和你一样,在学习 Python 的时候找了inject
等函数式方法。我很失望地发现它们并不都在那里,或者 Python 偏爱命令式方法。也就是说,如果你看的话,大多数构造都在那里。在某些情况下,图书馆会让事情变得更好。
对我来说有几个亮点:
您从 Ruby 中了解的函数式编程模式在 Python 中可用。他们只是看起来有点不同。比如有一个map函数:
def f(x):
return x + 1
map(f, [1, 2, 3]) # => [2, 3, 4]
同样,有一个reduce
function 可以折叠列表等。
也就是说,Python 缺少块,并且没有用于链接或组合函数的简化语法。 (对于没有块的好方法,请查看 Haskell 的丰富语法。)
出于某种原因,Python 社区似乎更喜欢命令式迭代,因为这些事情在 Ruby 中无需变异即可完成。例如,折叠(即inject
)通常使用命令式for
循环而不是reduce
来完成:
running_total = 0
for n in [1, 2, 3]:
running_total = running_total + n
这不仅仅是一个约定,Python 维护人员也加强了它。例如,Python 3 release notes 明确支持 for
循环而不是 reduce
:
如果你真的需要,请使用
functools.reduce()
;但是,在 99% 的情况下,显式for
循环更具可读性。
列表推导式是表达复杂函数操作的简洁方式(类似于 Haskell 的列表单子)。这些在 Ruby 中不可用,并且在某些情况下可能会有所帮助。例如,在一个字符串中查找所有回文的蛮力单行代码(假设您有一个函数 p()
为回文返回 true)如下所示:
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 年在这里写了更多的想法:The 'ugliness' of Python。鉴于当今对 ML 的关注,它们可能需要更新。
【讨论】:
叹息,我读了那篇文章,它证实了我的怀疑:很少有人了解 Python 中特殊方法的作用和实用性。它们非常有用且标准化,并且像这样强调它们以避免与它们经常实现的内置函数发生命名冲突。没有真正了解 Python 的人试图阻止他们的使用。 您似乎不了解方法的工作原理。方法本质上是一个函数,其第一个参数是该方法所属类的实例。当你写Class.method
时,方法是“未绑定的”,第一个参数应该是Class
实例;当您编写object.method
时,该方法将“绑定”到Class
的object
实例。这使您可以选择是每次使用映射(等)调用不同实例上的方法(传递未绑定的方法),还是保持实例固定并每次传递不同的第二个参数。两者都很有用。
你说得对,我不明白它们是如何工作的。自从发表这篇文章后,我对它有了更好的理解。谢谢!
[s[x:y] for x in range(l) for y in range(x,l+1) if p(s[x:y])]
- 这一行显示了 Python 的阅读难度。当您阅读 Ruby 代码时,您的视线从左向右移动,没有返回。但是要阅读 Python 代码,你需要去左-右-左-右-左-右...和圆括号,圆括号,圆括号,圆括号... 同样在 Python 中,您经常需要混合方法和函数。太疯狂了:E(C(A.B()).D())
而不是 Ruby 的 A.B.C.D.E
@Nakilon 这就是为什么你应该只对非常简单的情况使用嵌套列表推导,而不是像上面那样。编写一个查找字符串中所有回文的单行语句可能是“聪明的”,但最好保留用于代码高尔夫。对于其他人必须稍后阅读的真实代码,您只需编写几行函数。所以是的,那行很难阅读,但这是程序员的错,而不是语言的错。【参考方案3】:
我的建议:不要试图了解差异。了解如何在 Python 中解决问题。就像每个问题都有一个 Ruby 方法(考虑到语言的局限性和优势,它工作得很好),也有一个 Python 方法来解决这个问题。他们都是不同的。为了充分利用每种语言,您真的应该学习语言本身,而不仅仅是从一种语言“翻译”到另一种语言。
现在,话虽如此,差异将帮助您更快地适应并对 Python 程序进行一次修改。这对于开始写作来说很好。但是尝试从其他项目中学习架构和设计决策背后的原因,而不是语言语义背后的方式......
【讨论】:
感谢您的建议。我完全同意这种观点(我将其解释为“学习编程惯用的 Python”)。这正是我想要做的。我不是在问 “Ruby 的each
方法的 Python 名称是什么?” 我是在问 “在 Python 中如何正确完成与 Ruby 不同的事情,它们在哪里完成完全一样吗?” 如果 Python 的 false
实际上是 False
,那么知道我应该在何时何地以 Rubyesque 方式做事,以及何时何地不应该做同样重要。
@Phrogz:这很公平。我解释你的问题的方式是:让我们列出不同之处,这样我们就可以改变我们正在编程的语言。但这是一个公平的问题。我想我只是误解了你的要求。我将把它留在这里以供参考,但看看还有什么会很有趣......
我正在同时学习 python 和 ruby,在 web app dev 中我看到了更多的相似之处。【参考方案4】:
我对 Ruby 知之甚少,但这里有一些关于你提到的事情的要点:
nil
,表示缺少值的值,将是 None
(请注意,您可以像 x is None
或 x is not None
一样检查它,而不是使用 ==
- 或强制转换为布尔值,请参阅下一点)。
None
、零类数字(0
、0.0
、0j
(复数))和空集合([]
、
、set()
、空字符串""
等。 ) 被认为是虚假的,其他一切都被认为是真实的。
对于副作用,(for
-) 显式循环。为了生成一堆没有副作用的新东西,请使用列表推导(或其亲属 - 用于惰性一次性迭代器的生成器表达式,用于所述集合的 dict/set 推导)。
关于循环:你有for
,它在一个可迭代(!不计数)上运行,还有while
,它可以满足你的期望。由于对迭代器的广泛支持,fromer 的功能要强大得多。不仅几乎所有可以作为迭代器而不是列表的东西都是迭代器(至少在 Python 3 中 - 在 Python 2 中,您同时拥有两者,而且默认值是列表,遗憾的是)。使用迭代器的工具有很多——zip
并行迭代任意数量的可迭代对象,enumerate
为您提供 (index, item)
(在 任何 可迭代对象上,而不仅仅是在列表上),甚至切片 abritary (可能很大或无限)迭代!我发现这些使许多循环任务变得更加简单。不用说,它们可以很好地与列表推导、生成器表达式等集成。
【讨论】:
生成器表达式很酷。它们为 Python 提供了一些 Haskell 等语言的惰性求值功能。 @Clint:是的。并且完整的生成器功能更强大(尽管在大多数情况下都不需要简单的情况)。 您为什么要与x is None
或x is not None
核对?我总是与x == None
和x != None
联系。
@John:如果x
以愚蠢的方式定义__eq__
,它可能会给出误报。如果__eq__
的编程不够仔细,在给定特定值(即None
)时,它可能会崩溃(例如AttributeError
)。相反,is
不能被覆盖 - 它总是比较对象身份,这是检查单例的正确(最稳健、最简单和最干净)的方法。
@约翰。 “x is None”是绝对惯用的方法。 python.net/~goodger/projects/pycon/2007/idiomatic/handout.html【参考方案5】:
在 Ruby 中,实例变量和方法是完全不相关的,除非您明确地将它们与 attr_accessor 或类似的东西相关联。
在 Python 中,方法只是一类特殊的属性:一个可执行的。
例如:
>>> class foo:
... x = 5
... def y(): pass
...
>>> f = foo()
>>> type(f.x)
<type 'int'>
>>> type(f.y)
<type 'instancemethod'>
这种差异有很多含义,例如引用 f.x 是指方法对象,而不是调用它。此外,如您所见,f.x 默认是公共的,而在 Ruby 中,实例变量默认是私有的。
【讨论】:
其实我要说得更直白一点:在 Python 中,方法只是一种特殊的属性,而在 Ruby 中,属性只是一种特殊的方法。两种语言之间的一些重要对比特性不包括在内:Python 中的第一类函数和 Ruby 中的统一访问原则以上是关于从 Ruby 学习 Python;异同的主要内容,如果未能解决你的问题,请参考以下文章
FineBI学习系列之浅谈FineBI和Tableau对比异同(从产品理念和功能对比)(图文详解)