Re: Ruby and Python Compared

In regards to Ruby and Python Compared:

Larry Wall, the designer of Perl, has the slogan "There's more than one way to do it". In contrast, Bertrand Meyer, the designer of Eiffel, says "A programming language should provide one good way of performing any operation of interest; it should avoid providing two." Ruby follows the anarchist approach of Larry Wall, while Python follows the bondage-and- discipline approach of Eiffel.

Python is not in any way a B&D language. At all. Even slightly. No resemblence. It takes nothing from Eiffel. It probably could take some good ideas, but right now it doesn't even take any of its good ideas.

Python attempts to build consistency with a carrot approach, not a stick. I say "attempts" because there are many places where the Python community doesn't agree on a solution, doesn't agree on the phrasing of the problem, or simply doesn't know the best way to do things. There will always be such places, until such time as all programming problems are solved. Despite the futility of this effort, we still pursue compelling, complete, and consistent solutions.

An important rule in the Python community is: we are all consenting adults. That is, it is not the responsibility of the language designer or library author to keep people from doing bad things. It is their responsibility to prevent people doing bad things accidentally. But if you really want to do something bad, who are we to say you are wrong? It's your program. Maybe you even have a good reason.

That said, it's unlikely you'll see language designers or library authors going out of their way to enable you to do bad things where it was previously impossible. So some feature suggestions that are likely to cause errors are unlikely to get much consideration.

Python usually seems to have one best way to perform a given task, and even prescribes how the code is to be laid out, since indentation is syntactically significant.

Of course you'd have to be nutty not to indent your code the way Python wants you to. Python is DRY (Don't Repeat Yourself) here.

Ruby's treatment of Booleans is much cleaner. In ruby, false and nil are considered false in boolean contexts, and everything else is considered true. Python follows the antimathematical convention of C and Perl where 0 is considered false. This nonsense really has no place in such a high-level language. But Python makes things worse: empty lists, empty dictionaries, and empty tuples are false. What about empty sets? Well, sets are not a built-in type, so it depends on how they are implemented. Python lacks true booleans: true and false.

I don't think this is a very meaningful comparison. If you want to test for nil/None, test for nil/None. I guess you could say that Ruby is like Scheme or Smalltalk, and Python more like Lisp. There's points for and against each technique, but smart people have and continue to go both ways; just get used to it.

That False == 0 and True == 1 is an artifact of a time when Python lacked true booleans (though now True and False are "true" booleans, even if they are also integers: False is not 0). While the underlying integer nature of True and False lacks elegance, I haven't had any problems with it in practice.

As for other things being false (I use a lower-case "false" to refer to all falsish items in Python), this is just how Python is. Falseness is a property of an object. Empty sets are false, because thats how they are implemented. Convention, not the language, enforces the greater concept of what is "false".

In C, 7/3 is 2. I'm really a mathematician rather than a programmer, so I feel that 7/3 should be the rational number 7/3. The earlier versions of Python followed the C convention. Happily, Python has finally switched. Ruby defaults to the C convention but also gives the ordinary convention for rationals when you require the mathn standard library. I always require the mathn standard library!

In Python 7/3 is still 2. It probably will be until Python 3.0; Guido regrets the original decision, but changing it before 3.0 is way too big a problem. This isn't a reflection of some deep design principle in the language.

Python does not allow a library to change what 7/3 evaluates to. Here Python the language is taking over, where in Ruby a library can effect this. I assume in Ruby that means -- when the mathn library is loaded -- that division of all integers produces rationals...? But that seems so incredibly broken that I must assume it is not so.

Python's OO has become more extensive in recent versions, but it is still an OO bolt-on to a procedural language. Ruby is thoroughly OO. In Python, it is rather arbitrary whether some functionality is implemented by a function or a method (which have different syntaxes), and the programmer just has to remember how Python does it. Python has types and classes; in Ruby types are classes.

It's not "arbitrary," it's an aspect of whatever thing you are using. Not all libraries are designed the same, not all libraries are well designed, so sometimes a method is chosen when a function would do, or vice versa. You can make the same choices in Ruby. As a convention people in Ruby use methods far more often. Over time I personally have come to use functions more often than methods, only using methods if I see that a compelling object has emerged in my code. This is a difference of opinion. But Python isn't the only place where people are starting to feel more disaffected with class-based OO, though really it's an aside to the particulars of the language.

The distinction between classes (read: old-style classes) and types (read: built-in types, types written in C, new-style classes) is subtle and related to legacy. The distinction should disappear in 3.0, and you can mostly ignore it in current versions as well.

Until recently, Python's multiple inheritance had a critical design flaw which made it useless except as a way of implementing mixins. Whether the improved multiple inheritance will buy Python anything besides confusion has yet to be seen.

If it doesn't work for you, don't use it. In practice very few people use multiple inheritance anyway. Whatever.

But Ruby's thorough, dynamic OO gives the programmer incredible power. Methods can be easily added or removed from classes at run-time. They can easily added or removed from individual objects!

This can be done in Python as well, though it does not have the syntactic support that Ruby has. Some Python classes, including many built-in classes, are not possible to extend. Extending core classes is a highly questionable practice in my mind -- useful for clever hacks, but not for serious programming.

There is direct support for popular design patterns such as "Observer", "Delegator", "Singleton", "Visitor".

This is true; Python does not have any specific culture around these patterns. Though some patterns like "Singleton" are total nonsense anyway. But then, I don't really know what the support in Ruby looks like, so I don't know how I'd recognize it in Python.

A Ruby program has great capacity for "reflection", the ability to observe itself. Want a list of all living objects belonging to a particular class? No problem! Should you enjoy life in the fast lane, a ruby program can effectively rewrite itself at run-time.

Python's reflection is quite good. The list of all living objects, however, does not exist.

Python's garbage collection is based on reference- counting. Ruby's is mark-and-sweep (scheduled to be replaced by a generation-based system). Python's system allows the programmer more direct control over garbage collection: the del operator tells Python to garbage-collect a specific object right now. But Python's reference-counting system can easily lead to memory leaks, especially when trying to interface with C-code. Little rings of co-referential dead objects can accumulate with a long-running Python program.

This is not true.

Ruby is capable of Perl-like one-liners that can be used on the command line for system administration. Python is unsuitable for this.

Hey, one liners work in Python too. Generally speaking, Python is a very popular system administration language; certainly Perl is still king, but at least in the Linux world Python is the clear up-and-coming system administration language.

Created 12 Apr '06

Comments:

Singleton is Total Nonsense? Hmmm...

class _MasterRegistry(object):
""" This singleton holds all the class registries. There can be multiple registries to hold different unrelated sets of classes that reside in the same process. These registries are named with strings, and are created on demand. The MasterRegistry module global holds the singleton. """

Where have I seen that...

# Almad

A Ruby program has great capacity for "reflection", the ability to observe itself. Want a list of all living objects belonging to a particular class? No problem! Should you enjoy life in the fast lane, a ruby program can effectively rewrite itself at run-time.
Python's reflection is quite good. The list of all living objects, however, does not exist.
You can, however, make a class that can provide a list of all living instances. That's what I did when I hacked Routes support into web.py:
class controller(object):
    registry = {}
    class __metaclass__(type): 
        def __init__(cls, name, bases, dict): 
            if not ('register' in dict):
                cls.register(cls) 
            return
    @classmethod
    def register(cls, thecls):
        cls.registry[thecls.__name__] = thecls
        return
    @classmethod
    def subclasses(cls):
        return cls.registry.keys()
Yeah, it's ugly. But it works.
# Jon Rosebaugh

That's to find all subclasses, not all instances. I remembered about the gc module, though, and this kind of works:

def all_instances(a_class):
    return [obj for obj in gc.get_objects() if isinstance(obj, a_class)]

People use things like this to do reloading in Python. Once someone says "yes, I do reloading, and it works like a dream and never fails" then I'll be all over that. But it seems like there's some other tricks to do it right.

# Ian Bicking

Whups, yeah.

A similar principle would work, though.

# Jon Rosebaugh

"Yeah, it's ugly. But it works."

That's the point. Ruby does it with elegance.

# Dan Chokola

"As a convention people in Ruby use methods far more often."

Because there are no standalone functions in Ruby. Things like "puts" look like functions, but are actually instance methods of Kernel. http://dev.rubycentral.com/faq/rubyfaq-7.html

# Robert

"This nonsense really has no place in such a high-level language. But Python makes things worse: empty lists, empty dictionaries, and empty tuples are false. "

This is one of my favorite python features, the fact that "if something:" will always do the right thing. Isn't this the whole point of using a high level language in the first place? :-)

I don't want to write "if lst.is_empty?" or "if len(lst)" - oh wait, 0 shouldn't be False so this should be "if len(lst)==0", right?

# Justin

Hey, Ian, I already replied to this one on my blog too!

http://faassen.n--tree.net/blog/view/weblog/2006/04/12/0

# Martijn Faassen

Ian, It was gracious of you to analyze and counter oinkoink's article point-by-point, rather than just summing it up as a steaming pile of crap. :-)

Re: one-liners on the command line. I published a little script at the Cookbook site (pyline) that lets you write ad-hoc CLI scripts in Python, suitable for piping. E.g. to print the md5 sum of all .py files in the current directory, you could (use md5sum, or you could) write:

ls *.py | pyline -m md5 "'%s %s' % (md5.new(file(line).read()).hexdigest(), line)"

Prettier than Perl, though not by much. But Python is certainly suitable for CLI use.

# Graham

>>> from __future__ import division
>>> 7/3
2.3333333333333335

not exactly a library, but it's a provisional feature

# Moe Aboulkheir

In Python 7/3 is still 2. It probably will be until Python 3.0; Guido regrets the original decision, but changing it before 3.0 is way too big a problem. This isn't a reflection of some deep design principle in the language.

Python does not allow a library to change what 7/3 evaluates to. Here Python the language is taking over, where in Ruby a library can effect this.

This program:

from __future__ import division

print 7/3

prints out 2.33333333 in Python 2.3 and newer. And 7//3 gives the old division behaviour which produces 2.

# hanz

But it won't change the behaviour of / operator in other modules.

# Baczek

I guess you could say that Ruby is like Scheme or Smalltalk, and Python more like Lisp.

This is incorrect. Ruby's setup is similar to Scheme's, except that NIL is not false in Scheme. However, in Lisp, NIL is the only false value, and there is no boolean type, with T used to represent truth only by convention. This is not in any way similar to Python's behavior.

# Nick Thomas

This article was thoroughly disappointing, especially given Mr. Bicking's involvement in the Python community. I wrote a full response to this article here.

# Dan Chokola