the wee hours
27 July 2006
Tim gives mad props. My favorite bit:
Maybe the single biggest advantage is readability. Once you’ve got over the hump of the block/yield idiom, I find that a chunk of Ruby code shouts its meaning out louder and clearer than any other language. Anything that increases maintainability is a pearl beyond price. [emphasis mine]
In theory, Python ought to do better, lacking all those silly end statements cluttering up the screen. But in practice, when I look at Python code I find my eyes distracted by a barrage of underscores and double-quote marks. Typography is an important component of human communication, and Ruby’s, on balance, is cleaner.
As someone who uses Python in his day job, and Ruby whenever I can fit it in (for very small values of t), I have to agree. Python can be readable, and having blocks be delimited by indention levels is not always a bad thing. But it can get rather hairy when lines wrap, or when you try to make lines fit neatly in a terminal window using the ’\’ line continuation marker. Then your nice indentation gets a little harder to “just see”.
One thing I do about Python is list comprehensions, i.e. taking a list or array, and then mapping each element in it to a function. Here’s an example:
n = [1, 2, 3, 4] p = [ x*2 for x in n ]
Pythonistas will note that the list comprehension is a shorthand for the map built-in function:
p = map(lambda x: x*2, n)
As much as I like list comprehensions, I find myself agreeing that Ruby is a bit more descriptive (and easier to read) than Python. After six months of using Python in my day job, I still find lambdas in Python to be syntactically icky.
Even though Ruby doesn’t have list comprehensions, it’s version of Array.map is easier to read then both in my opinion:
n = [1, 2, 3, 4]
p = n.map { |x| x*2 }
You can make the Ruby version a bit more verbose by using do ... end instead of the curly braces:
p = n.map do |x| x*2 end
Most Rubyists would prefer the former version over the latter, for brevity’s sake.
Python also gives us filter to filter an enumerable object, but again, you have to use lambda unless you’ve defined a function elsewhere:
f = filter(lambda x: x>2, n)
Ruby gives us Array.select:
f = n.select { |x| x>2 }
Conversely, there is also Array.reject, which does the opposite of Array.accept:
g = n.reject { |x| x>2 }
One last thing. Suppose you want to do the union of two arrays. In Python, you have to make the lists sets by casting (ugh) and, if you want to keep the mutable list, you have to recast it back to a list (double ugh).
a = ['foo', 'bar', 'baz'] b = ['quux', 'bar'] c = list(set(a) | set(b))
In Ruby, you get the logical operations on Arrays for free:
c = a | b
That’s it; either way, you get ['foo', 'bar', 'baz', 'quux'] (although not necessarily in that order).
At the end of the day, I’m happy using either language over pretty much anything else.
(But I not so secretly heart Ruby.)
UPDATE: Silly me, you can use a list comprehension in Python to save yourself a lambda for filter:
[ x*2 for x in n if x>2 ]
Thanks to my coworkers for reminding me of this.
UPDATE 2: Duh. Array.select, not Array.accept.
Comments
30 July, 07:44 PM
Andrzej:
Well, I don’t mind self except perhaps as a forced argument in instance methods.
Did you mean Array.select.
Yes. Doh! /me corrects.
4 August, 02:54 AM
I’m pretty sure that instead of this in Python:
f = filter(lambda x: x>2, n)
you can do this:
f = [ x for x in n if x > 2 ]
Personally I think Python’s list comprehensions are far more readable than any of that .map or .select stuff with the lambda function passed in.
4 August, 02:55 AM
Doh! I should have read your “UPDATE:”. Sorry!
7 August, 04:39 PM
I think list comprehensions are now “passee” in python land, due to the new generator expressions.
But there are still some places where python shines in readability over ruby, for example doing a find_all+map is more ugly than a generator expression, python’s keyword arguments are much better than ruby’s use-an-hash-handling-it-somehow, and I love the cleanness of unbound methods in things like array.sort(key=Person.name).
Tradeoffs, sadly :)
Andrzej
30 July, 11:08 AM
Nice article.
I also use both Python and Ruby. Generally, I like both of them, however while I prefer the Python indentations I dislike the Python’s self keyword everywhere.
What’s your opinion on the the usage of self keyword in Python?
You wrote:
“Ruby gives us Array.accept:”
Did you mean Array.select ?
Andrzej