Wednesday, June 13, 2007

Ruby: A Python Programmer's Perspective

As a "language lawyer", it's fun to learn new languages and see how they differ in subtle ways. Here are some of the many ways Ruby is different from Python, etc. Most of these aren't necessarily good or bad, they're just different. Looking at the differences, it's fun to try to peek into the design decisions behind the languages. If you've noticed more interesting differences, post them below as comments!

In Ruby, Classes, modules, and constants must begin with an upper case letter. Actually, this reminds me of Haskell.

Ruby uses "end" instead of indentation. That's fine unless you're a Python programmer like me who keeps forgetting to type "end" ;)

Ruby doesn't have true keyword arguments like Python. Instead, if you pass ":symbol => value" pairs to a function, they get put into a single hash. Python can act like Ruby using the "**kargs" syntax, but Ruby cannot act like Python; it cannot explicitly declare which keyword arguments are acceptable in the function signature.

Ruby is like Perl in that the last value of a function is its implicit return value. In Python, if there isn't an explicit return statement, the return value is implicitly None.

Ruby does not make a distinction between expressions and statements like Python does. Hence, you can do:

  a = if 5 > 6
7
else
puts "hi"
end
This is like Scheme and ML.

Ruby is much more clever than Python at figuring out how to translate end of lines into statements. For instance, the following works in Ruby, but not in Python.

  a = 2 +
2
Python would require parenthesis.

I'm still trying to figure out the proper style for when you should use parenthesis in function calls and when you should leave them out in Ruby. The distinction is idiomatic.

Ruby's string interpolation syntax is '"foo #{2 + 2}"'. Python uses '"foo %s" % (2 + 2,)'.

The syntax for declaring a class method (what Java calls a static method) is strange, since Python uses "self" for something very different:

  def self.my_class_method
puts "hi"
end
I must admit that "@a" is easier to type than Python's "self.a" without any significant loss in readability.

Single quoted strings in Ruby are like single quoted strings in Perl or like raw strings in Python. They get less interpretation.

Instance variables are never directly accessible outside the class in Ruby, unlike in Python or even Java.

In Python, you may use a publically accessible member on day one and change it to a property on day two behind everyone else's back. In Ruby, you use an attribute on day one. Fortunately, the syntax is very convenient, "attr_accessor :name". This is much more succinct that explicit getters and setters in Java.

Ruby has protected and private members, which Python and Perl purposely chose to leave out. A private member in Ruby is even more private that a private member in Java. If you have two instances of the same class, in Java one instance can access the other instance's private members, but that's not true in Ruby.

Ruby uses modules as mixins instead of using multiple inheritance to support mixins.

Ruby embraces what Python calls "monkey patching".

Python programmers generally try to avoid using eval, but I don't think that's the case in Ruby.

Ruby uses to "items << item" to append to a list. Python uses "items.append(item). PHP uses "items[] = item". This is one place where every language does it differently.

Ruby has "%w { foo bar bat }", which is like Perl's "q( foo bar bat )". Python doesn't have a construct for this, but you can use "'foo bar bat'.split()".

Ruby makes heavy use of symbols, like Scheme and Erlang (which calls them atoms). Python doesn't have symbols, so strings are used instead.

Ruby uses "elsif", whereas Python uses "elif".

Ruby doesn't need a colon in control structures, but it does require an end of line or a semicolon. Hence, to do a one liner in Python, it's:

  if 11 > 10: print "yep"
whereas in Ruby it's:
  if 11 > 10; puts "yep"; end
As everyone knows, Ruby supports blocks. Personally the use of "|a, b|" to denote arguments to the block seems really strange to me. Who uses's pipes for arguments? They don't even pair like parenthesis do!

In Python, there's a much stronger emphasis on passing functions. I'm sure that it's possible in Ruby, but it's more natural to pass a block instead.

Ruby has a very different syntax for exceptions handling than most languages:

  begin
a = some_func
rescue FuncFailed
puts "I'm hosed!"
end
When you unmarshal an object in Ruby, all the class definitions have to be loaded already. Python will import them for you, assuming they can be imported.

Ruby allows function names like "empty!" and "empty?" which is clearly a matter of taste, but I like it. This is probably inspired by Scheme.

For some reason, it seems like using "help(String)" in Ruby is pretty slow, whereas using "help(str)" is pretty fast. I wonder if Ruby doesn't have the docstrings attached to the object at runtime like Python does. In Python, this stuff is always loaded unless you use "-00" for optimization.

The rest of these comments are inspired by: http://books.rubyveil.com/books/ThingsNewcomersShouldKnow

What Ruby calls "'foo'[0]", Python calls "ord('foo'[0])". What Python calls "'foo'[0]", Ruby calls "'foo'[0,1]" or "'foo'[0].chr".

In Ruby, a failed lookup in a hash returns a default value, which is usually nil. You can set a different default if you want. Python will raise an exception if you try to access a key that doesn't exist. However, in Python 2.5, you can now set a default value for the dict. Now I know where they got that idea from ;)

In Ruby, you say "(hash[key] ||= []) << value", whereas in Python, you say "hash.setdefault(key, []).append(value)."

In Python, it's "len(obj)" (i.e. len is a generic function). In Ruby, it's "obj.length" (i.e. polymorphism is used). This difference seems to happen a lot.

In Ruby, strings are mutable. Hence, "s.upcase" returns a upcase version of s, whereas "s.upcase!" actually modifies s. Python strings are immutable.

Ruby doesn't have tuples (i.e. immutable arrays).

Because Ruby doesn't have as strong a notion of immutable objects as Python does. For instance, you may use mutable objects as hash keys in Ruby. Python forbids this. If you do change the value of a key in Ruby, you may want to let the hash recalculate the hash values for all the keys via "my_hash.rehash".

Ruby will let you assign a new value to a variable defined outside your scope:

  i = 0
(0..2).each do |i|
puts "inside block: i = #{i}"
end
puts "outside block: i = #{i}" # -> 'outside block: i = 2'
This was not previously possible in Python without using a workaround. However, Python is gaining this feature with the use of a keyword similar to the "global" keyword.

Coming at it from a different angle, in Java, you use explicit variable declarations to assign scope. This is true too in Perl when you use "my". In Python, an assignment automatically sets scope. Hence, you can shadow variables in Java, Python, and Perl. Ruby tries really hard to avoid shadowing. Hence, whoever assigns to the variable first sets the scope. Shadowing can still happen in rare cases (see here), but it's a lot less likely.

Ruby assignments are expressions. Hence, you can do:

  while line = gets
puts line
end
Python purposely left this out because it's too easy to confuse "=" and "==". Hence, in Python you would write:
  for line in sys.stdin:
print line
Ruby has two sets of logical operators. They have different precedences. Hence, "a = b && c" means "a = (b && c)", whereas "a = b and c" means "(a = b) and c". I'm going to agree with Guido on this one and say this is just too confusing.

Ruby has an === operator and case statements. This feature is a lot closer to the match feature in ML languages than anything in Python:

  case my_var
when MyClass
puts "my_var is an instance of MyClass"
when /foo/
puts "my_var matches the regex"
end
This is really neat. Notice that, thankfully, Ruby doesn't require "break" like all the C inspired languages.

In Ruby, only false and nil are considered as false in a Boolean expression. In particular, 0 (zero), "" or '' (empty string), [] (empty array), and {} (empty hash) are all considered as true.

In Python, 0, "", '', [], and {} are all considered False. In general, Python has a very rich notion of "truthiness", and you can define the "truthiness" of your own objects.

In Ruby, if s is a string, you may write "s += 'a'" or "s << 'a'". The first creates a new object. The second modifies s. If you modify s, that may "surprise" other pieces of code that also have a reference to s. Python strings are simply immutable so this can't happen.

Ok, that's all for now! In my opinion, they're both great languages. If you know about more fun differences, post them below!

47 comments:

Noel Rappin said...

Hi JJ -- welcome to the Ruby side.

I've been alternating between Python and Ruby now for a couple of years... couple of comments:

The pipe |a, b| syntax for block arguments is taken from Smalltalk. If you've never played with a Smalltalk environment, you should try one. I bet you'd like it.

Things I like about Ruby with respect to Python

1. I think Ruby is the only language that gets accessors right. The thing you want to do 95% of the time is trivial, and the thing you want to do 5% of the time is a pretty easy override.

2. Blocks. I find that my kind of functional style flows pretty easily in Ruby. It's also not hard to pass named functions.

3. Being able to add functions to existing classes. Although I know this really bugs some people.

4. Expression syntax. The implicit return in Python always gets me. Plus I like writing a ternary operator as an actual if statement


Things I like about Python with respect to Ruby

1. Consistency. Python enforces it. As a result, my Python code is more likely to be readable by me six months later. Some parts of Ruby are still a little too Perlish...

2. Real keyword arguments (coming to Ruby soon). The Ruby magic of gathering up map pairs into a hash is sort of annoying.

3. The fact that you need to both "require" and "include" a Ruby module to use it as a mixin...

4. On balance, I prefer Python immutable strings to Ruby's mutable string/symbol split.

Hmm... this is probably long enough for me to make it a blog post of my own...

Shannon -jj Behrens said...

Thanks, Noel! Happy Hacking!

chris said...

Fun to read! One minor comment, in ruby you can also do this:

puts "yep" if 11 > 10

wolverian said...

I think you meant "qw(foo bar baz)".

Personally I think the lack of explicit scoping is an embarassing bug and makes both Python and Ruby broken.

Anonymous said...

"Ruby will let you assign a new value to a variable defined outside your scope: ..."

This is called a true closure (notably present in lisps, javascript, lua, io, smalltalk, etc) which python doesn't support. It has nothing to do with python's _global_ keyword (though _local_ is considered for python 3000)

jwithers said...

In respect to adding functions to existing classes in ruby, you can do that in python as well, either to a class or an instance. It is just considered bad form unless you have an exceptionally good reason for it.


import new
class Foo:

def __init__(self):
self.x = 2

def double(self):
print self.x*2

def triple(self):
print self.x*3

if __name__ == '__main__':

# Attach method to class at runtime
Foo.double = double

f = Foo()
f.single()
f.double()

# Attach method to instance at runtime
f.triple = new.instancemethod(triple,
f, f.__class__)
f.triple()

Anonymous said...

"adding functions to existing classes/instances"

I think the case was:

s = "foo"

class << s
def bar
# ...
end
end

Bill Mill said...

If the self.* really bothers you, you can use _ instead of self.

At the expense of every python programmer laughing at you :)

rgh said...

It's nice to see a blog post about two different languages that doesn't rant about one language being better than the next. As you said they are "... they're both great languages ...".

Like your work.

Chris McDonough said...

Thanks a lot for this, very useful!

Jeff said...

Nice informative article.

Does Ruby have anything equivalent to Python's 'in' operator? Currently, I'm using something like:

%w[one two three].include?('two')

but to me it doesn't seem as readable as:

'two' in ['one', 'two', 'three']

Anonymous said...

Ruby doesn't need a colon in control structures, but it does require an end of line or a semicolon. Hence, to do a one liner in Python, it's:

if 11 > 10: print "yep"

whereas in Ruby it's:

if 11 > 10; puts "yep"; end


Actually, in Ruby you can also do:

if 11 > 10 then puts "yep" end

so no EOL or semicolon is necessary

Antonio said...

There's no direct equivalent to the in operator that I know of, but it's relatively simple to duplicate it:

class Object
def in?(arr)
arr.include?(self)
end
end

That lets you do:
'two'.in?(['one','two','three'])

With a little more magic, you can achieve a syntax whereby you can do:
'two'.in? 'one', 'two', 'three'
(Using *args.)

Naturally, since it's Ruby, you can leave off the parens in the former case, as well, making it look perhaps a little more natural. As a bonus, this will give the method to all objects, and it will work for any parameter that responds to `include?' (which, amongst other things, includes String).

Anonymous said...

Ruby can do the '%s' style string interpolation as well:

irb(main):004:0> "foo %s" % (2 + 2)
=> "foo 4"

AndrewO said...

Very cogent analysis. All too often comparisons between Python and Ruby turn out to be total flamebait. Rubyists and Pythonists should be friends!

jeff: that's the standard way of doing it. I like the Ruby way, but I can understand your tastes differing.

Ruby tries to minimize the number of operators, since for the most part they're implemented as methods. E.g. "foo = Foo.new" instead of the more Java/C++ style "foo = new Foo". Hence "include?(obj)" is a method of the Enumerable module which is mixed in to anything that holds things like Array and Hash.

Another example of this are the standard arimethic operators. The Ruby parser goes to great lengths to make "3 + 4" a convenient shorthand "3.+(4)" (where "+" is an instance method of the Numeric class).

Once again -- great post.

Anonymous said...

" 1. I think Ruby is the only language that gets accessors right. "

Lisp, anyone?

Anonymous said...

puts 'yep' if 11 > 10


Btw you forgot to mention alias an alias_method

they are GREAT

you cant do this in python :)

Shannon -jj Behrens said...

Hey, everyone, thanks for all your comments!

QuickRedFox said...

Just to be like everyone else, instead of if 11>10; puts 'Yep' etcc... in ruby you could say:

puts 'yep' if 11> 10

Mark Thomas said...

You say that Ruby doesn't have tuples, but it has something more powerful: freeze, which can make anything immutable, even your own objects.

tuple = ['a','b','c'].freeze

Anonymous said...

The 'freeze' method is good, but coming from Java, I do have problems with strings not being immutable by default, and I agree that changing keys in hashes is problematic. I do enjoy coding in Ruby more then Java so I can handle these issues.

fusiongyro said...

Ruby can also use a loop keyword on the right side of an expression:

x += 1 while x < 10

Ruby has negated conditional and indefinite iteration keywords. In English: unless, and until. So you can write these kinds of blocks:

unless player.is_dead
...
end

until graph.all_nodes_colored
...
end

And these can of course be used on the right side of an expression.

Antonio said...

I had a sudden hackish idea that you might be able to make all Strings immutable by reopening String.new and making it freeze all strings, but unfortunately most methods in String (and the String literal form itself) bypass String.new. Oh well, it was a horrid idea to begin with :-P

tsal said...

Nice article!

I tend to like Python more than Ruby, but one thing I "get" better in Ruby is meta-programming.

I really like the "method_missing" way of doing things. I know there's ways to do it in Python, but I still haven't wrapped my head around the "how".

For example, Ruby:

class Blah
  def method_missing(method, *args)
    puts "No method called #{method} here."
  end
end

And in Python:

class Blah(object):
  def __getattr__(self, name):
    print "No method called %s here." % (name,)

In Ruby, I can whip up a proxy object that makes calls based on a regexp match of the method name. I could probably do the same in Python, but I'm still learning. :)

Jay said...

To bring up the if statement syntax again, this is also a valid Ruby if-statement one-liner:

puts "Hello world" if x

Jay said...

"For some reason, it seems like using "help(String)" in Ruby is pretty slow, whereas using "help(str)" is pretty fast. I wonder if Ruby doesn't have the docstrings attached to the object at runtime like Python does. In Python, this stuff is always loaded unless you use "-00" for optimization."

We have the "ri" and "gem_server" commands. :)

Gregory Brown said...

This post is pretty good. It'd be nice if you turned it into a quick reference, maybe as a PDF or HTML table that should the differences side by side.

Would be useful whenever one needs to quickly see the differences for various common language features.

Shannon -jj Behrens said...

Gregory, I agree. It'd probably take a lot of time, though. Just writing the post took me four hours! ;)

the daniel said...

I'm a little more comfortable with Ruby than Python -- here's something I discovered while writing some python code last night:

In Ruby, you can do set operations on normal arrays:

irb> [1, 2, 3] - [1, 2]
=> [3]

Whereas in Python it seems you have to be sure to turn those arrays into set objects:

>>> set([1, 2, 3]) - set([1,2])
set([3])

Gregory Brown said...

If you'd like, I can try to put something together based on your post sometime soon, giving you credit of course..

Shannon -jj Behrens said...

the daniel:
If you want to do set operations, you really *should use* a set since it's optimized for that. Figuring out if an item is in a set is O(1). Figuring out if an item is in an array is O(N).

Shannon -jj Behrens said...

> If you'd like, I can try to put something together based on your post sometime soon, giving you credit of course..

I'm sure there are people out there who would be thankful. I'm sorry I don't have much time to help. You don't have to give me all the credit. A simple acknowledgement with my name and email would be sufficient. Thanks!

Shannon -jj Behrens said...

Ruby's exception handling mechanism has an else clause like Python :)

Shannon -jj Behrens said...

In Python, you can create class methods and then access them indirectly from an instance "obj.class_method"

In Ruby, you cannot access class methods from an instance "obj.class_method". You have to access the class method through the class itself.

Anonymous said...

Being able to call class object method on a class instance is a very very bad thing.

Shannon -jj Behrens said...

> Being able to call class object method on a class instance is a very very bad thing.

Why?

Anonymous said...

Because classes and their instances are separate objects. It makes all things much less consistent to add such an artificial rule (not to mention namespace pollution). Btw, it greatly reminds all the inconsistencies in java/c++ etc. You won't see such a warts in pure OO languages like e.g. Smalltalk. Scala, otoh, takes different approach using singletons (declared by the _object_ keyword).

jrodman said...

It sounds like your rule is the artificial one, anonymous. It's a very simple mechanism. There are functions/methods, there are arguments. You can call methods on whatever makes sense. Simple.

Shannon -jj Behrens said...

See Part II here: http://jjinux.blogspot.com/2009/02/ruby-python-programmers-perspective.html

Eric.Le.Bigot said...

About symbols not being in Python: doesn't the intern() function provide something similar? (http://docs.python.org/dev/library/functions.html) I'm just asking, as I'm not too much familiar with it... :)

In addition to this, id("hello") produces the same result every time I run it in the Python 2.5 shell: it looks like the string is in effect a symbol, no?

Shannon -jj Behrens said...

Yes, the intern function exists. It works and it's good. I blogged about it here: http://jjinux.blogspot.com/2008/08/python-intern.html

> In addition to this, id("hello") produces the same result every time I run it in the Python 2.5 shell: it looks like the string is in effect a symbol, no?

It breaks if you do something like:

a = 'hello'
b = 'hel'
b += 'lo'
id(a) == id(b) # Returns False

panzi said...

@alias_method: If I understand this correctly you can do this like this:

class A:
  def foo(self):
    return "something"

class B:
  bar = A.foo
  def foo(self):
    return self.bar() + " else"

You can always call a method explicitly:

class B:
  def foo(self):
    return A.foo(self) + " else"

panzi said...

In my example I meant to write "class B(A):" but because of duck typing it actually doesn't matter.

Shannon -jj Behrens said...

Actually, it does matter. It works if B is a subclass of A, but not otherwise:

>>> class A:
... def foo(self):
... print "foo"
...
>>> class B:
... def foo(self):
... A.foo(self)
...
>>> b = B()
>>> b.foo()
Traceback (most recent call last):
File "", line 1, in
File "", line 3, in foo
TypeError: unbound method foo() must be called with A instance as first argument (got B instance instead)

Hari said...

> What Ruby calls "'foo'[0]", Python calls "ord('foo'[0])". What Python calls "'foo'[0]", Ruby calls "'foo'[0,1]" or "'foo'[0].chr".

What does this mean? Both Python and Ruby returns "f" as a string (str/String) for "'foo'[0]".

Shannon -jj Behrens said...

> What does this mean? Both Python and Ruby returns "f" as a string (str/String) for "'foo'[0]".

Ah, Ruby fixed this in Ruby 1.9. Indexing a string used to return the ord of the character in Ruby 1.8. Now, it returns a one character string. I think that's a lot more useful.

Here's the old Ruby and then the new Ruby:


dhcp-172-19-63-56:~ jjinux$ irb
irb(main):001:0> "foo"[0]
=> "f"
irb(main):002:0>
dhcp-172-19-63-56:~ jjinux$ which irb
/opt/local/bin/irb
dhcp-172-19-63-56:~ jjinux$ /usr/bin/irb
>> "foo"[0]
=> 102

Shannon -jj Behrens said...

Sorry, I got the order mixed up. The first one is Ruby 1.9. The next one is Ruby 1.8.