I see Ruby people bashing Python a lot on Stackoverflow. It’s a bit sad, Python and Ruby are usually bestest friends forevah. So I started a question on Stackoverflow, not about which of them is best, but just what the differences are, objectively.
It was more difficult to make that objective than I thought, but I sure learnt a lot. And, as suspected, many of the differences are just semantic, like Ruby using @attribute and Python using self.attribute. And of course it’s objectively it’s impossible to say what language is better, which we of course knew from the beginning. That’s no surprise. The surprise is that there are so few differences that can be cast as better or worse at all. Even such things that sound like an objective good or bad, such as a language having a certain feature, turns out to mostly be a matter of taste.
I could just find these differences that in any way can be called objectively better:
1. Ruby has a reference to the class in the class body
This means that code like this is possible:
class MyClass
initialize_magick()
end
While in Python, you would have to write:
class MyClass:
pass
initialize_macgick(MyClass)
As you see the practical difference is very small. But Rubys variant is cleaner, as the magic stuff is done in the class definition, so you see that it’s being done when you look at the class. And indeed, the Zope component architecture uses some heavy magic with metaclasses to make the same thing happen in Python:
def MyClass:
implements(IMyInterface)
So it’s possible (In Python 2) by some clever tricks to get the same effect. In Python 3 it isn’t because the metaclass syntax is different, so in Python 3 you instead need to use class decorators. And what you think is cleaner of
@magickly_initialized
def MyClass:
pass
or
def MyClass
initialize_magick()
end
is again a matter of taste. So, a small plus for Ruby there, without a doubt. But it’s really not a big deal, because calling the initialize method after the class or as a decorator is really not a major drawback.
2. Ruby has continuations
Continuations are heavy magic. And Ruby has them, and Python has not. You can “fake” them, and it seems that many of the usecases can instead be done with passing variables into .next() which you can do in Python 3.1. The use of continuations seem rather limited, and hard to understand, but it’s there in Ruby, and not in Python. So another plus for Ruby.
3. Python has generators
You can fake them in Ruby by using blocks, or use continuations. So no big deal again, but a small plus for Python anyway, as Pythons generators are both easy and clean.
4. Python has multiple inheritance
Some people claim multiple inheritance is evil. But having it means that if you think it’s evil, you can simply not use it. I have used it a lot, never found it a problem, but I think adaptation is a better pattern. Still, it’s clearly a plus for Python.
5. Python has docstrings
Docstrings makes it possible to attach documentation directly to the classes and methods. That’sa nice documentation plus, and makes things like the Python interpreters help() function really useful. Another plus.
Aaaaannnd, that’s it!
These five small differences are the only things that I can see are objective improvements over the other language. And as you see, none of them are really major. It’s just small things that are more convenient or not. So, am I gonna switch to Ruby now, when it’s almost just as good? Nope. Because there are lot of other differences, that are differences in attitudes and semantics. And there I really like Python better. Here are three major features of Ruby that in my opinion either are misfeatures or just not of value:
1. Ruby has built-in regexp and line input looping
This is inspired by Perl, and means you can easily make a script that looks through a text file and munges each line separately and prints out another text line. This is extremely useful’in many cases. Also Ruby is said to be better to make glue.scripts, so it can more readily be used for a shell script replacement than Python. Plus for Ruby? Well, no, not in my book. I do these things sometimes, mostly in awk. If they get more complex than that, I prefer to use the clarity of Python than the obscure machinations of regexps. And having a small open(filename).readlines() isn’t that much trouble. So this is a feature I think only adds clutterm and I’m happy Python doesn’t have it. Wow! That turned out to be a subjective issue! That surprised me.
2. Ruby protects class attributes etc
Ruby treats variable names that has the first letter in capitals like a constant, so you can’t modify it. You also can’t access attributes of objects from outside the class. Python doesn’t protect your code from other programmers. If they want to shoot themselves in the foot they are allowed to. I like that. I remember when using Delphi, and I needed to subclass an object in the standard library, but some attributes or methods were protected, so I couldn’t. I ended up having to duplicate almost the whole (rather big) component class in my subclass, just because the developer of the base class hadn’t forseen my type or requirement. So these sorts of protections are generally just a problem. OK, it’s less of a problem in Ruby, as it allows you to monkey-patch the class but this still is a big plus for Python in my book. I’m sure Ruby people do not agree. It’s subjective again.
3. Ruby has blocks
But some differences are more differences in attitudes than anything else. And sometimes these different attitudes seem to in the end be the same attitude but from different angles. Ruby has for example the concept of blocks. Blocks seems to pretty much be Rubys name for closures, and it’s an abstract concept basically meaning a piece of code that gets variables from somewhere else and can have local variables that doesn’t pollute the surrounding name space.
Rubys blocks are widely hailed (by Ruby people) as something fantastic compared to Python. But it turned out to be very difficult to find actual cases where Ruby really did something in a cleaner way than Python thanks to blocks. Well, it turns out that Python also has blocks. But they are called “functions”. What’s the difference? Functions need a name, while blocks don’t. At this point a Python programmer says “Aha, so blocks are lambdas!” Yes, except that blocks can be multiline code and do anything, while Pythons lambdas are restricted to being expressions.
OK, so plus one to Ruby then? Well, no. Because what use is a block if you don’t attach it to a variable name so you can call it? In Ruby you create blocks and use them directly like this:
amethod do |variable|
print variable
end
This is principally equivalent to Pythons
def theblock(variable)
print variable
amethod(theblock)
Which of course is doable in Ruby as well. Which way is neater? Matter of taste again. And it becomes even harder to decide when considering that Ruby has a special statement to call the block with parameters. It’s called yield. Yield exists in Python as well, but there it’s used for generators. But the we get this code examples:
def themethod
for a in [1,2,3,4,5]
yield a
end
end
themethod do |b|
puts b
end
Which is almost suspisciously similar to
def themethod():
for a in [1,2,3,4,5]:
yield a
for b in themethod():
print b
So in the end, this much touted principle of block seem to very rarely actually lead to any significantly different code than in Python. So here are widely discussed differences in principals that in the end…end up as the same thing (almost)!
4. Python has list comprehensions
It’s worth mentioning here that Python has some big features that Ruby doesn’t, like for example list comprehensions.
[foo(x) for x in alist if bar(x) != 'frotz']
But although I use them all the time because they are practical I don’t know what I think about them. Well, it’s shorter than:
foo = []
for x in alist:
if bar(x) != 'frotz':
foo.append(x)
But is it really neater and more readable? I’m not sure. It becomes a matter of taste again. I did not like list comprehensions when they first arrived in Python, but of course now I’ve gotten used to them. So that is not a good argument for Python. It’s subjective again.
5. Python has decorators
Decorators are prettier than the other ways of doing the same in Python, for example the above initialize_magick(class). But again it can be argued that the @decorator syntax just makes Python ugly, and that you don’t need them as much in Ruby. Again, matters of taste.
So why Python?
So, in the end, why do I, subjectively prefer Python over Ruby in almost everything? Because of Pythons clarity and simplicity, and it’s attitudes and principles. In Ruby, you have open classes, so you can extend any class. In Python, you can monkey patch in extensions into any class. The difference? That in Python it’s an ugly attitude that is frowned upon. In Ruby, many people tutorials and examples of blocks extend the Array class, like brutally monkey patching the standard types is a normal thing to do. (A hint guys: That does NOT help in explaining blocks, it’s just confusing).
In Ruby, there are no functions, but there are methods, blocks and lambdas. All of them seem to have subtle differences, and sometimes you need to not just call them, but use .call(). In Python, there are only functions. Lambdas are functions whose __name__ is <lambda>. Methods are functions that are wrapped to you don’t have to pass self. Simple and clear.
Python is more explicit. In Ruby you can skip the () when calling a method. Rubyists like that because it makes calls look like statements they say. Pythonists like me think it’s a bad idea to make a call look like a statement. If it’s a call it should bloody well look like one.
And Pythons packages and modules are just so much better for namespaces than Rubys modules. Again, subjectively. And you can choose if you want to import * in Python or not. In Ruby that’s all you can do
And all of these things, the things that makes me go “Eww…” about Ruby and “Yeah!” about Python turns out not to be features, but just subjective options of the different languages semantics and attitudes. It is truly so that none of the Languages are better than the other.
Except, of course, that Ruby sucks.