Ruby有什么是Python没有的,反之亦然?

263 投票
36 回答
136911 浏览
提问于 2025-04-15 12:50

关于Python和Ruby的讨论很多,但我觉得这些讨论都没什么帮助,因为大家总是在说某个特性在某种语言中不好,或者说某种语言没有某个特性,实际上它是有的。我也知道自己为什么更喜欢Python,但这也是个人的主观感受,对别人选择语言没有帮助,因为他们的开发喜好可能和我不一样。

所以,客观地列出它们的不同之处会很有意思。不要说“Python的lambda不好”,而是要解释Ruby的lambda能做什么而Python做不到。不要带主观色彩,提供示例代码是很好的!

每个回答中不要列出多个不同之处,请投票支持你知道是正确的回答,反对那些你知道不正确(或主观)的回答。此外,语法上的差异不太有趣。我们知道Python用缩进来处理,而Ruby用括号和结束符,@在Python中叫self。

更新:现在这是一个社区维基,我们可以在这里添加主要的区别。

Ruby在类体内有类的引用

在Ruby中,你可以在类体内直接引用类(self)。而在Python中,直到类构造完成后才有类的引用。

举个例子:

class Kaka
  puts self
end

这里的self就是类,这段代码会打印出“Kaka”。在Python中,无法在类定义体内(方法定义外)打印类名或以其他方式访问类。

Ruby中的所有类都是可变的

这让你可以对核心类进行扩展。以下是一个Rails扩展的例子:

class String
  def starts_with?(other)
    head = self[0, other.length]
    head == other
  end
end

在Python中(假设没有''.startswith方法):

def starts_with(s, prefix):
    return s[:len(prefix)] == prefix

你可以在任何序列上使用它(不仅仅是字符串)。为了使用它,你需要显式导入,比如from some_module import starts_with

Ruby有类似Perl的脚本功能

Ruby有一流的正则表达式、$变量、逐行输入循环等功能,这些让它更适合编写小型的shell脚本,用于处理文本文件或作为其他程序的粘合代码。

Ruby有一流的继续执行功能

这得益于callcc语句。在Python中,你可以通过各种技术创建继续执行,但语言本身并没有内置支持。

Ruby有块(blocks)

通过“do”语句,你可以在Ruby中创建一个多行的匿名函数,这个函数会作为参数传递给前面的函数,并从那里调用。在Python中,你可以通过传递一个方法或使用生成器来实现。

Ruby:

amethod { |here|
    many=lines+of+code
    goes(here)
}

Python(Ruby的块对应于Python中的不同结构):

with amethod() as here: # `amethod() is a context manager
    many=lines+of+code
    goes(here)

或者

for here in amethod(): # `amethod()` is an iterable
    many=lines+of+code
    goes(here)

或者

def function(here):
    many=lines+of+code
    goes(here)

amethod(function)     # `function` is a callback

有趣的是,Ruby中调用块的便利语句叫“yield”,而在Python中则会创建一个生成器。

Ruby:

def themethod
    yield 5
end

themethod do |foo|
    puts foo
end

Python:

def themethod():
    yield 5

for foo in themethod():
    print foo

虽然原理不同,但结果非常相似。

Ruby更容易支持函数式编程(管道式)

myList.map(&:description).reject(&:empty?).join("\n")

Python:

descriptions = (f.description() for f in mylist)
"\n".join(filter(len, descriptions))

Python有内置生成器(用法类似于Ruby的块,如上所述)

Python在语言中支持生成器。在Ruby 1.8中,你可以使用生成器模块,它利用继续执行来从块创建生成器。或者,你可以直接使用块/proc/lambda!此外,在Ruby 1.9中,Fibers可以作为生成器使用,Enumerator类是一个内置生成器 4

docs.python.org有这个生成器的例子:

def reverse(data):
    for index in range(len(data)-1, -1, -1):
        yield data[index]

与上面的块示例对比。

Python有灵活的命名空间处理

在Ruby中,当你用require导入一个文件时,那个文件中定义的所有内容都会进入你的全局命名空间。这会导致命名空间污染。解决这个问题的方法是Ruby的模块。但如果你用模块创建了一个命名空间,那么你必须使用那个命名空间来访问里面的类。

在Python中,文件就是一个模块,你可以用from themodule import *导入里面的所有名称,这样就会污染命名空间。如果你只想导入特定的名称,可以用from themodule import aname, another,或者简单地import themodule,然后用themodule.aname访问名称。如果你想在命名空间中有更多层次,可以使用包,这些包是包含模块和一个__init__.py文件的目录。

Python有文档字符串

文档字符串是附加在模块、函数和方法上的字符串,可以在运行时进行检查。这有助于创建帮助命令和自动文档。

def frobnicate(bar):
    """frobnicate takes a bar and frobnicates it

       >>> bar = Bar()
       >>> bar.is_frobnicated()
       False
       >>> frobnicate(bar)
       >>> bar.is_frobnicated()
       True
    """

Ruby的对应物类似于javadocs,位于方法上方而不是内部。它们可以在运行时通过使用1.9的Method#source_location从文件中检索 示例用法

Python支持多重继承

Ruby则不支持(“故意”这样做——见Ruby官网,这里看看Ruby是怎么做的)。它确实重用了模块的概念作为一种抽象类。

Python有列表/字典推导式

Python:

res = [x*x for x in range(1, 10)]

Ruby:

res = (0..9).map { |x| x * x }

Python:

>>> (x*x for x in range(10))
<generator object <genexpr> at 0xb7c1ccd4>
>>> list(_)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Ruby:

p = proc { |x| x * x }
(0..9).map(&p)

Python 2.7+

>>> {x:str(y*y) for x,y in {1:2, 3:4}.items()}
{1: '4', 3: '16'}

Ruby:

>> Hash[{1=>2, 3=>4}.map{|x,y| [x,(y*y).to_s]}]
=> {1=>"4", 3=>"16"}

Python有装饰器

Ruby中也可以创建类似装饰器的东西,而且可以说它们在Ruby中并不是那么必要。

语法差异

Ruby需要用“end”或“}”来关闭所有的作用域,而Python仅使用空格。最近Ruby也有尝试允许仅用空格缩进 http://github.com/michaeledgar/seamless

36 个回答

28

Python 示例

在Python中,函数是第一类变量。这意味着你可以定义一个函数,把它当作一个对象传来传去,甚至可以覆盖它:

def func(): print "hello"
def another_func(f): f()
another_func(func)

def func2(): print "goodbye"
func = func2

这是现代脚本语言的一个基本特性。JavaScript和Lua也是这样做的。而Ruby就不这样处理函数;在Ruby中,给一个函数命名就相当于调用它。

当然,在Ruby中也有一些方法可以实现类似的功能,但这些方法并不是第一类操作。比如,你可以用Proc.new把一个函数包裹起来,这样就可以把它当作变量使用——但这样它就不再是一个函数了,而是一个带有“call”方法的对象。

Ruby的函数不是第一类对象

Ruby中的函数不是第一类对象。函数必须被包裹在一个对象中才能传递;而这个结果对象不能像函数那样使用。函数不能以第一类的方式被赋值;相反,你必须在它所在的容器对象中调用这个函数才能对它进行修改。

def func; p "Hello" end
def another_func(f); method(f)[] end
another_func(:func)      # => "Hello"

def func2; print "Goodbye!"
self.class.send(:define_method, :func, method(:func2))
func                     # => "Goodbye!"

method(:func).owner      # => Object
func                     # => "Goodbye!"
self.func                # => "Goodbye!"    
34

Ruby 里有一个叫做 的概念,简单来说,它就是一段代码的语法糖;可以把它理解为一种创建闭包的方法,并把它传递给其他方法,这些方法可能会使用这个块,也可能不会。你可以通过 yield 语句在后面某个时候调用这个块。

举个例子,Array 类里一个简单的 each 方法的定义可能是这样的:

class Array
  def each
    for i in self  
      yield(i)     # If a block has been passed, control will be passed here.
    end  
  end  
end  

然后你可以这样调用它:

# Add five to each element.
[1, 2, 3, 4].each{ |e| puts e + 5 }
> [6, 7, 8, 9]

Python 有匿名函数、闭包和 lambda,但它没有块的概念,因为缺少一些方便的语法糖。不过,至少有一种方法可以临时实现类似的功能。比如,你可以看看 这里

5

在Ruby和Python中,你可以在类的定义里写代码。不过,在Ruby里,你可以用一个叫做self的东西来指代这个类。而在Python里,因为这个类还没有定义,所以你不能用类似的方式来指代它。

举个例子:

class Kaka
  puts self
end

在这个例子中,self就是这个类,运行这段代码会输出"Kaka"。在Python中,你无法通过类的定义部分来打印类名或者以其他方式访问这个类。

撰写回答