从 Ruby 中学习 Python; 异同

我很了解露比。我相信我现在可能需要学习 Python。对于那些知道两者的人来说,两者之间有什么相似的概念,又有什么不同呢?

我正在寻找一个类似于我为 为 JavaScripters 学习 Lua写的入门书的列表: 像空格重要性和循环结构这样的简单事情; Python 中 nil的名称,以及什么值被认为是“真实的”; 使用 mapeach的等价物是惯用的,还是 咕哝 一些关于理解力的东西 咕哝的标准?

如果我得到了各种各样的答案,我很乐意将它们聚合成一个社区维基。或者你们可以互相争斗,试图创建一个真正全面的清单。

编辑 : 明确地说,我的目标是“适当的”和惯用的 Python。如果有相当于 inject的 Python,但是没有人使用它,因为有一种更好的/不同的方法来实现迭代一个列表并在这个过程中积累结果的通用功能,我想知道你是如何做事情的。也许我会用一个常见目标列表来更新这个问题,比如在 Ruby 中如何实现这些目标,以及在 Python 中如何实现这些目标。

31369 次浏览

我的建议是: 不要试图了解这些差异。学习如何在 Python 中处理这个问题。就像每个问题都有一种 Ruby 方法(考虑到语言的局限性和优势,这种方法非常有效)一样,这个问题也有一种 Python 方法。他们都不一样。为了充分利用每种语言,你真的应该学习语言本身,而不仅仅是从一种语言到另一种语言的“翻译”。

既然如此,这种差异将帮助您更快地进行调整,并对 Python 程序进行1次修改。这对于开始写作来说是个好的开始。但是尝试从其他项目中学习体系结构和设计决策背后的原因,而不是语言的语义背后的原因。

以下是我认为的一些关键区别:

  1. Ruby 有块,Python 没有。

  2. Python 有函数,而 Ruby 没有。在 Python 中,可以将任何函数或方法传递给另一个函数。在 Ruby 中,一切都是一个方法,不能直接传递方法。相反,您必须将它们包装在 Proc’s 中才能传递它们。

  3. Ruby 和 Python 都支持闭包,但方式不同。在 Python 中,您可以在另一个函数中定义一个函数。内部函数具有对外部函数变量的读访问权,但不具有写访问权。在 Ruby 中,使用块定义闭包。闭包对来自外部作用域的变量具有完全的读写访问权限。

  4. 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 还提供了类似的地图理解。

  5. Python 支持元组,而 Ruby 不支持。

  6. Ruby 支持 switch/case 语句,而 Python 不支持。

  7. Ruby 支持标准的 expr ? val1 : val2三元运算符,而 Python 不支持。

  8. Ruby 只支持单一继承。如果需要模拟多重继承,可以定义模块并使用 mix-in 将模块方法拉入类中。Python 支持多重继承而不是模块混合。

  9. Python 只支持单行 lambda 函数。Ruby 块(类似于 lambda 函数)可以是任意大小的。正因为如此,Ruby 代码通常是以比 Python 代码更具功能性的风格编写的。例如,要在 Ruby 中循环一个列表,通常需要这样做

    collection.each do |value|
    ...
    end
    

    该块的工作方式非常类似于传递给 collection.each的函数。如果你要在 Python 中做同样的事情,你必须定义一个命名的内部函数,然后把它传递给每个方法的集合(如果 list 支持这个方法的话) :

    def some_operation(value):
    ...
    
    
    collection.each(some_operation)
    

    因此,在 Python 中通常会使用以下非函数方法:

    for value in collection:
    ...
    
  10. Using resources in a safe way is quite different between the two languages. Here, the problem is that you want to allocate some resource (open a file, obtain a database cursor, etc), perform some arbitrary operation on it, and then close it in a safe manner even if an exception occurs.

    In Ruby, because blocks are so easy to use (see #9), you would typically code this pattern as a method that takes a block for the arbitrary operation to perform on the resource.

    In Python, passing in a function for the arbitrary action is a little clunkier since you have to write a named, inner function (see #9). Instead, Python uses a with statement for safe resource handling. See How do I correctly clean up a Python object? for more details.

我认识小露比,但是关于你提到的事情,这里有几个要点:

  • nil,表示缺少值的值,应该是 None(注意,您检查它的方式类似于 x is Nonex is not None,而不是使用 ==-或者通过强制布尔值,参见下一点)。
  • None、零式数字(00.00j(复数))和空集合([]{}set()、空字符串 ""等)被认为是假的,其他一切都被认为是真的。
  • 对于副作用,(for -)显式循环。为了生成一组没有副作用的新东西,可以使用列表理解(对于懒惰的一次性迭代器,可以使用它们的亲属生成器表达式,对于所述集合,可以使用 dict/set 理解)。

关于循环: 您有 for,它在迭代器(!没有计数) ,和 while,这样做你会期望。由于对迭代器的广泛支持,前者的功能要强大得多。不仅几乎所有可以作为迭代器而不是列表的东西都是迭代器(至少在 Python 3中是这样的——在 Python 2中,两者都有,遗憾的是,缺省值是列表)。有很多工具可以用来处理迭代器—— zip并行迭代任意数量的迭代器,enumerate给你 (index, item)(在 任何上迭代器,不仅仅是在列表上) ,甚至可以切片任意(可能很大或者无限大)的迭代器!我发现这些使得许多循环任务变得更加简单。不用说,它们与列表理解、生成器表达式等集成得很好。

在 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 时也在寻找 inject和其他函数式方法。我很失望地发现它们并不都在,或者 Python 喜欢命令式方法。也就是说,大多数构造都在那里。在某些情况下,图书馆会使事情变得更好。

给我几个亮点:

  • 您从 Ruby 中了解到的函数式编程模式在 Python 中是可用的。只是看起来有点不一样。例如,有一个 map 函数:

      def f(x):
    return x + 1
    
    
    map(f, [1, 2, 3]) # => [2, 3, 4]
    

    类似地,有一个 reduce功能可以折叠列表,等等。

    也就是说,Python 缺少块,没有用于链接或组合函数的流线型语法。(要了解不使用块的方法,请查看 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(); 然而,显式的 for循环在99% 的情况下更具可读性。

  • 列表理解是表示复杂函数操作的一种简洁方法(类似于 Haskell 的 List monad)。这些在 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 的“丑陋”。鉴于当今对机器学习的关注,它们可能需要更新。