CNK's blog

Python vs. Ruby

Intro

Ruby and Rails are my web tools of choice. But I work in a software development group who largely work in Python with a collection of home-built libraries. So I really enjoyed Mike Leone’s “Python for Ruby Programmers” at last year’s LA Ruby Conference. His talk emphasized similaries between the two languages - which was helpful to me since I had gotten caught up in the small but annoying syntax differences - like semantic whitespace and the need for ‘self.’ and ‘()’ all over the place. With a more balanced perspective, I have been reexamining the pros and cons of the Python language.

Explicit: One of Python’s principles is: Explicit is better than implicit. One manifisation of that principle is having to type ‘self’ as the first argument when defining an instance methods (and ‘cls’ when defining class methods). Kind of annoying but whatever.

Scoping: In Ruby, the class keyword starts a new scope. So any variables created between ‘class’ and it’s matching ‘end’ are local to that scope. Most of the time this leads to the behavior one wants - but if you want something different, then you need to use some of Ruby’s metaprogramming tricks such as class_eval to get around that change of scope.

In Python, ‘class’ does not create a scope for the names used inside the bodies of methods. So when you want to refer to an instance’s attributes, you need to say ‘self.attribute_name’ rather than ‘attribute_name’ (or @attribute_name in Ruby). That’s annoying for the common taste of instance attributes. But the lack of new scope for classes makes it possible for instances to access class attributes using the same syntax you would use for an instance’s own properties. (The example below is adapted from “Python Essential Reference (4th edition)” by David M. Beazley)

class Account(object):
num_accounts = 0

def __init__(self, name, balance):
    self.name = name
    self.balance = balance
    Account.num_accounts += 1

def __del__(self):
    Account.num_accounts -= 1

def inquiry(self):
    return self.balance


$ python
>>> from account import Account
>>> Account.num_accounts
0
>>> a = Account('a', 100)
>>> a.num_accounts
1
>>> Account.num_accounts
1
>>> c = Account('c', 300)
>>> Account.num_accounts
2
>>> a.num_accounts
2
>>> #### but if I assign to the instances's attribute, then we get
#### separate attributes - one for the class and one for the instance

>>> a.num_accounts = 3
>>> Account.num_accounts
2
>>> a.num_accounts
3
>>> a.__dict__
{'balance': 100, 'name': 'a'}

Another interesting thing from this example is that in Python one can easily access methods that are called during the object’s life cycle. The double underscores in the method names tell you that you are getting kind of into the guts of an object, but you can get to them fairly directly. I tried rewriting the example above in Ruby. It’s quite easy to increment a class variable or class instance variable when you create an object. But where can you decrement it when an object is destroyed? Can one explicitly destroy an object in Ruby? Not directly. In fact, this StackOverflow thread suggests if you want to track all Account instances, you should use the WeakRef class from the Ruby standard library:

def all_instances
   # this will vacuum out the dead references and return the remainder.
   @@weakrefs_to_vehicles = @@weakrefs_to_vehicles.select(&:weakref_alive?)
end

def total_vehicles
   all_instances.count
end

Comments