First thoughts on Ruby: A quick showdown vs. Python
Contents
Python and Ruby always struck me as somewhat related. They’re both dynamic languages from the 1990s, still highly popular, and influenced a number of more modern dynamic programming languages like Julia. They’re similar enough in spirit that one can easily fall into the trap of thinking that if they know one of Python and Ruby, they don’t have to learn the other. I was primarily a Python programmer for my research work, and Python is popular in academia, so I am definitely guilty of having a closed mind toward Ruby.
Today I made an effort to challenge my assumptions and give Ruby a serious shot.
I finished the Ruby koans, which took me about five hours, and four hours of real work
.
I’d messed with Try Ruby before that, but that didn’t take me very far, and RubyMonk felt too slow and clunky for my tastes when I tried the primer.
So the natural thing to do for me is to compare Ruby with Python, and see what I like and don’t like about each of them. I’m not trying to start a flamewar, it’s more like I’m just trying to translate some of my knowledge and highlight some of the differences that I saw so far.
Toy script comparisons
I wrote some toy scripts to show the most jarring differences so far. Yeah, I’m sure I’ll dig further into the language and all of this code will look silly, but as a budding Rubyist this is about as advanced as I can muster.
Running sum
In Ruby, the tendency is to iterate over a number of variables with the #each
method, and then you pass in a block that gets called on each of the elements.
What’s immediately noticeable here is that Ruby blocks are way more powerful than Python’s anonymous callalbles (lambdas
).
#!/usr/bin/env ruby
= []
running_sum = []
args
[1,5,7].each do |x|
<< x
args if running_sum.size > 0 then
<< running_sum.last + x
running_sum else
<< x
running_sum end
end
puts "Running sum: #{running_sum} from #{args}."
#!/usr/bin/env python3
= []
running_sum = []
args
for n in [1, 5, 7]:
args.append(args)if running_sum:
-1] + n)
running_sum.append(running_sum[else:
running_sum.append(n)
print("Running sum: {} from {}.".format(running_sum, args))
At face value, the two aren’t very different, but these examples illustrate a fundamental difference between the attitudes toward iteration: Python uses for
loops for everything, but Rubyists will do everything in their power to avoid using them.
OOP
Ruby classes seem to be both less verbose and syntactically simpler than Python classes.
Both languages have a message passing
way of calling instance methods: in Python, with getattr(object, name)
and in Ruby via #send()
.
Mixins, via modules
, are the coolest advantage Ruby seems to have over Python, where the closest equivalent is boring old implementation inheritance.
Both languages have an equivalent for getters and setters. In Python, this is done via a decorator, but in Ruby it seems to be a language builtin.
#!/usr/bin/env ruby
module Nameable
def name=(name)
puts "changed name to #{name}"
@name = name
end
def name
@name
end
end
class Dog
include Nameable
def initialize(name)
@name = name
end
def bark
puts 'Woof!'
end
end
= Dog.new('Fido')
fido .send('bark')
fido.name = 'Cheese'
fidoputs fido.name
#!/usr/bin/env python3
class Nameable(object):
@property
def name(self):
return self._name
@name.setter
def name(self, value):
print('Changed name to {}'.format(value))
self._name = value
class Dog(Nameable):
def __init__(self, name):
self._name = name
def bark(self):
print("Woof!")
= Dog('Fido')
fido
fido.bark()= 'Cheese'
fido.name print(fido.name)
Conclusion
My initial impression of Ruby is quite positive.
It’s a strong candidate for replacing my one off Python scripts
that often felt a bit too verbose for the tasks they were achieving.
While I’m not comfortable enough to do serious development in it yet, I do get the feeling that Ruby will be quite readable over the equivalent Bash/Awk/Sed mashup that I might otherwise use to avoid writing a wordy Python script that uses subprocess
.
There’s a distinct impression that Ruby is more fun
than Python.
There are many ways to achieve certain tasks, and the general goal is to find the most beautiful way.
In Python, it seems like there’s a heavier focus on readability.
I was skimming through some of the Rails codebase, and Rubyists seem to have no qualms about using the obscure ||=
operator, in spite of the fact that it really doesn’t mean what you think it means.
I think the most obscure syntactic construct in Python is just the wacky ternary operator.
I do not believe that Python and Ruby are really playing in the same niche. While Python dominates in academia and scientific programming, Ruby seems to win in the Perl-ish systems scripting and web development areas (though Django and Tornado put out some serious competition with Rails and Sinatra, and Python’s Pelican looks like a fine alternative to Jekyll).