Brandon J. Van Every said:
I'm realizing I didn't frame my question well.
What's ***TOTALLY COMPELLING*** about Ruby over Python? What makes you jump
up in your chair and scream "Wow! Ruby has *that*? That is SO FRICKIN'
COOL!!! ***MAN*** that would save me a buttload of work and make my life
sooooo much easier!"
As opposed to minor differences of this feature here, that feature there.
Variations on style are of no interest to me. I'm coming at this from a C++
background where even C# looks like an improvement. ;-) From 10,000 miles
up, is there anything about Ruby that's a "big deal" compared to Python?
One person mentioned Japanese documentation. I'm sure that's Totally Kewl
to the Japanese....
Negative points earned by using 'frickin' and 'kewl'.
But with an earnest answer to your question - I started evaluating
Ruby about two and a half years ago. For me at the time, the "big
deals" in comparison to Python were:
A unified class hierarchy.
==========================
Python 2.2 started addressing this issue. And it's not one that
absolutely needs to be solved. But it starts making things more
consistent, and we can finally extend core types such as lists and
dictionaries, and legally write new code in C that can be subclassed
in Python.
Ruby has a different concept of modules than Python. In Ruby, a
Module is usually a class-like object that is mixed in, allowing for
multiple inheritance type functionality in a single inheritance
system. What's nice is that Ruby offers some very nice common built
in modules that you can reuse in your own classes, such as
'Comparable' (offering overloads for comparison operators),
'Enumerable' (offering a lot of Python built-in functions as methods
for collections of objects - equivalents of map(), min(), max(),
etc...), and more.
A pure pure pure OO system is a nice dream, and this feature makes
Ruby closer in it's OO offerings to Smalltalk than to Python. But I
have to admit that I actually prefer Python's module/package based
system better (I'll address this further down).
getter/setter methods for object attribute access
=================================================
This is another feature offered by Python 2.2 and later, in new style
classes. A problem that plagued Zope for a while was one didn't know
if a certain common attribute would be an attribute, or a method -
foo.title versus foo.title(). Some objects want to compute their
title, or other attributes (ie - you may store a mailbox size
attribute as bytes, but present it as megabyte to make it easier to
view/edit - so why not compute?). Since Python 2.2 now offers this
feature, which is not a critical one (just one that's quite nice),
Ruby doesn't score too many points here any more either. But it was
something that I liked when first evaluating the language.
Static/Class methods
====================
Python 2.2 and later finally allow these as well. In a pure OO system
like Ruby and Java, static method are very important when offering
what would be module-level functions in Python. Basically, they're
methods defined in a class that don't operate on an instance
(typically the 'self' in Python).
Class Methods operate on a class object instead of an instance.
Between static and class methods, for example, you can define new
constructors and place them in the class itself (not possible in older
Python or old style classes). So what Ruby, Java, Objective-C, and
other languages can offer are things like::
MyString.initFromFile(somefile)
MyString.initFromURL(someurl)
which would generate a new 'MyString' instance based on the contents
of a file or URL. Python 2.2 has caught up on this feature as well.
As others have stated - it's certainly *prettier* to define these
types of methods in Ruby, but I imagine Python will improve in this
area as time goes by. It's just nice to have these features
available.
Generators
==========
Another cool feature that Ruby had that fascinated me when I looked at
it that Python 2.2 coincidentally picked up. This is the 'yield'
keyword, and it produces a lot of opportunities for lazily generating
data to be iterated through. Again - Python 2.2 and later have this
one, so it's a moot point now. But this was something that really
excited me about Ruby when I saw it.
'blocks'
========
blocks...closures...anonymous functions. Whatever you want to call
them, Ruby employs them throughout the language. These have been
discussed en masse in this thread, I'm sure. It's no longer a deal
breaker for me. For the most part, if a lambda or list comprehension
expression don't offer me enough, writing a real named Python function
is the better route to go, in my opinion.
You can do a lot with lamda: expressions, and many parts of the system
allow them (including the re module, where you can supply a function
that takes a regex match object as its argument and do some
calculations on it).
'I'm some flan & I'm < half off.'
Of course, for any detailed function, it's still better to write a
full one:
.... """ look up values we know names for before using ord() """
.... good_values = {
.... ' ': ' ', '<': '<', '>': '>', '"': '"'
.... }
.... match = matchobj.group(0)
.... return good_values.get(match, "&#%s;" % ord(match))
.... I'm some flan & I'm < half off.
In-Place versus Copy
====================
There's a long running Python situation where one wants a sorted list
in the middle of an expression, for the sake of the expression only.
Python only lets you do "foo.sort()" as a statement on its own. If
you use this in an expression, it evaluates to "None", not a sorted
list. Little battles over this show up on occasion in the Python
world and have since the beginning of time.
In Ruby, there's a difference between "foo.sort" and "foo.sort!"
(parenthesis aren't required on method calls in Ruby). Ruby can use
some punctuation in its method names, and the exclamation point is
often used to indicate that this method will change the current
object. "foo.sort!" in Ruby is equivalent to "foo.sort()" in Python
(assuming 'foo' is a list).
This isn't a huge dealbreaker in favor of Ruby. It's just nice. The
use of punctuation to differentiate between inplace operations and
i'll-return-a-new-modified-copy operations is a nice one. (Ruby also
can have method names end in '?', which is often used for boolean
methods. 'foo.isEmpty?' for example.)
Ruby Issues and Python Strengths
================================
Ruby has a lot more issues for me now, and Python has some really
strong features that differentiate it. Some of these features, in
fact, go almost unnoticed because they're so smooth and make so much
sense (see coming remarks about modules).
Ruby is still more closely related to Perl than it is to Python. Ruby
uses all of those shortcut expressions that I find terrible, like $_.
Ruby uses other punctuation marks to mark what different variables,
with name, $name, @name, and @@name all being different. While it's
nice to differentiate between local, global, instance, and class
variables, all of these extra punctuation 'shortcuts' get in the way
of readability. In fact, it looks like it's quite easy to write
unreadable code in Ruby. There are just lots of punctuation marks one
has to get used to, and while it's easy to get used to them, I think
it moves Ruby away from the "executable pseudocode" that most decent
Python code can be. You wind up with statements like ``%w{@names}``
or ``print if ($. == 1)``. dollar-dot? what's that? Fortunately,
Python's general "explicit is better than implicit" design means I
never have to know.
Ruby still has too much shell-script nature inside of it. One built
in Ruby expression type is command expansion, or `foo`, where foo is a
shell command. (You can also use %x, as in %x{echo "Hello There"}).
Putting `uname -v` in your code will execute the Unix 'uname' command.
The exit status of the command is in the cheerful global variable $?.
As a result, I wonder how well Ruby works as an embedded language.
Python and its sibling Jython have been embedded in a variety of
systems to allow programmability. Some examples of this include 3D
environments (I think 'Caligari Truespace' is one of these), UML
tools, and more. In such systems, you don't want shell commands to be
run. Yet Ruby has them as first class expressions. Python places
operating system interaction into the flexible 'os' and 'os.path'
modules, and there are the newer 'popenN' modules as well for running
other processes from Python and getting the results back in. But -
it's a very key feature of Python that it is not bound strongly to the
Unix shell in any way. I don't know how much the `command` feature of
Ruby is used, but it's still troubling (to me) that it is a first
class expression.
Python has modules. I love this fact about it. It got even better
when multi-level packages were added as a core feature to the system.
I don't know why, but using ``import package.module`` or ``from
package.module import Class`` makes me so much happier than the
alternatives I see in Ruby, Perl, and PHP. But Python modules and
packages just add a nicer element to the language, and I believe that
Modula 2 and/or 3 was one of the linguistic influences on Python.
Ruby has two statements, and they just bother me. They seem to be
nowhere nearly as clean as Python modules (or even Java packages):
'load' and 'require'. And they seem to behave slightly differently.
load "filename.rb" "includes the name Ruby source file every time the
method is executed". I wonder if that's a full path expression, like
if you could do ``load "mypackage/file.rb"``, and if that expression
would fail on Windows or the classic Mac OS (or any other operating
system that uses a different path separator than Unix). Python
Modules are free of that.
require "filename" seems not to require the '.rb' extension, and seems
closer to Python's imports, but I don't think it puts anything into a
special namespace. With Python, you have to explicitly load blindly
into your namespace with the "from foo import *" syntax. But it looks
like if you do a ``require 'tk'`` command in Ruby, you're doing the
equivalent of "from tk import *".
Ruby has modules, but in a completely different sense than Python.
Modules are namespaces defined within files, which can also be used as
mix-ins to classes. While Python makes all subelement traversal use
the '.' syntax (package.module.Class, instance.someMethod, etc), Ruby,
adds more punctuation to get into modules, using the :: syntax
(NameSpace::Example::CONST or NameSpace::Example.new). Man, the more
I write, the more I really appreciate the simplicity of Python. We
will do funny things with __underscores__, but even that's pretty
consistent.
Python also has the nice feature of compiling/caching modules, which
can speed things up. I don't know if Ruby re-evaluates all code every
time you start a Ruby program up, but I haven't really seen an
equivalent to Python's '.pyc' and '.pyo' (optimized, generated when
running with the -O option in Python) files.
Ruby does have some nice things going for it, and I think that people
looking for a good object oriented Perl would be happy with it.
Personally, I think that Python is more mature, has better design
influences (Modula and ABC instead of Perl and Smalltalk). A lot of
the extraordinary features of Ruby seem to have found their way into
recent Python versions. Python's built in OS abstraction layers (the
'os' module, etc) makes it easier to write multiplatform code (made
even easier by universal newline support in Python 2.3, which helps
deal with the "'\r', '\n', or '\r\n'?" situation). Python's explicit
nature makes following code a lot easier - it's *usually* pretty easy
to tell where a name is coming from, for instance. And it does all of
this without abusing the sunday comic curse word character set
{$@#:!}. Ruby also seems to have some other implicit behaviors,
like::
def mult(n, fact)
n * fact
end
the last statement in an expression is what's returned. You *can* use
a return statement. But it's nice that Python always requires it
(otherwise, you get 'None' as a result):
def mult(n, fact):
return n * fact
it's just a nice touch. It's especially nice when you have to go into
systems like Java and you have variables being used that take a while
to process - "where's this name coming from? Oh, it's somewhere
within the class, not local!". self.doThis() is much more
understandable than just doThis(), especially when you start to stack
up namespaces.
In summary - there's nothing totally compelling about Ruby over Python
for me, especially since Python 2.2 and 2.3 have come along and
answered my own petty grievances. There are some cool features and
aspects of Ruby, to be sure, but I'm pretty happy where I am.
And Python always equals = batteries included!
92.7 times out of 103.5, there's a module that shipped with the
distribution that will do something that you need to do. Time and
again, I hear stories of "I was just about to write a whole blablabla
module when it turns out that one was already there!"