Geeks With Blogs
Adventures of a Setup Developer my musings about setups and other things in my life

I had been experimenting with Ruby/Rake. I took a little while for me to get adjusted to Ruby's syntax. I love closures in Ruby and their effective use in Rake. I was trying if I could do the same in Python. As always, I came up with a very shabby code as the first attempt.

#run target.py
# the global dict to store targets
targets={}
#defining and adding a target
class Target:
    def __init__(self,name='',dependencies=[],fn=None):
        self.name=name
        self.dependencies=dependencies
        self.fn=fn
        self.done=False
        self.success=False

    def run(self):
        if(not self.done):
            self.success=self.fn(self)
            self.done=True
        return self.success

    def describetarget(self):
        print self.fn.__doc__

def addtarget(name,dependencies,callable):
    objtarget=Target(name,dependencies,callable)
    targets[name]=objtarget

# now to add the actual target

def sayHello(target):
    '''Prints hello world with the calling targets' name'''
    print "hello world from " + target.name
    return True
addtarget('sayhello',[],sayHello)

#run the target
targets['sayhello'].run()

As you can clearly see, you would have to first define the function and add it later as the target. For simplicity sake have not written the logic of constructing the dependency tree. You can see that this is nowhere close to writing the same code in Ruby. Consider the following.

Python Code:

def sayHello(target):
    '''Prints hello world with the calling targets' name'''
    print "hello world from " + target.name
    return True
addtarget('sayhello',[],sayHello)

Versus

Rake Code:

task :mytask do |t|
print 'hello world ' + t.name
end

The magic of ruby code is in closures. Python does support closures. Python 2.4 also has a new feature called decorators which can do the same thing. This is what I did in Python using decorators.

#runtargetwithdecorator.py
#global dict to store targets
targets={}

#the target class
class Target:
    def __init__(self,name='',dependencies=[],fn=None):
        self.name=name
        self.dependencies=dependencies
        self.fn=fn
        self.done=False
        self.success=False

    def run(self):
        if(not self.done):
            self.success=self.fn(self)
            self.done=True
        return self.success

#the target decorator function

def target(name,dependencies=[]):
    def decorator(f):
        mytarget=Target(name,dependencies,f)
        targets[name]=mytarget
        return f
    return decorator

#implementing the decorator
@target('mytarget')
def MyFunction(target):
    print "hello world", target.name
    return True


targets['mytarget'].run()

Now consider the following.

Python code:

@target('mytarget')
def MyFunction(target):
   print "hello world", target.name
   return True

versus

Ruby code:

task :mytask do |t|
   print 'hello world ' + t.name
end

Now the python code is a lot more easlier to read than its counterpart in Ruby. Python may not be an effective DSL for build languages but is definitely a lot easier to read and understand than Ruby's code. But you cannot have a empty defaults target defined in python. You should have a valid callable after the @decorator. For example, to my knowledge, you cannot write the following code in python without the following function.

@target('default',['mytarget1','mytarget2'])
def somefn(target):
     pass

But you can write the following code in ruby without any closure method.

task :default => [:mytask1,:mytask2]

In spite of Rake's simplicity, I still would prefer writing Python code. I guess it is just my personal preference. No offence to Rake fans. :-) 

Posted on Thursday, September 1, 2005 12:40 PM Open Source | Back to top


Comments on this post: Ruby/Rake and Python Decorators

# re: Ruby/Rake and Python Decorators
Requesting Gravatar...
strange - to me, this is a perfect example why Ruby is more powerful than Python.
Left by murphy on Oct 01, 2005 1:10 AM

# re: Ruby/Rake and Python Decorators
Requesting Gravatar...
Yes. I perfectly agree. Ruby is a lot more powerful than Python. I just was figuring out how to use Python decorators to have a similar look and feel to that of Ruby's closures. I live in India and it is pretty damn hard to get hold of any of the Ruby books. On the other hand we have Python books in every bookstore. Its a shame isn't it? That is why Python is a lot more popular in India than Ruby.
Left by Vagmi Mudumbai on Oct 02, 2005 10:20 AM

# re: Ruby/Rake and Python Decorators
Requesting Gravatar...
Actually, Python does have closures. It gets wonky with immutable types ( such as integers and strings) and the assignment operator "=" both creates a reference and sets the value.

If the item referenced in the clousre is a mutable object, the python closure will work just fine.

There is an exact term for this, but I don't recall what it is.
Left by Daniel on Feb 01, 2006 10:46 PM

# re: Ruby/Rake and Python Decorators
Requesting Gravatar...
The Ruby code is much more readable to me, simply because I don't know what that Python snippet does without reading up on it - it's not self-explanatory in any way.

The Ruby code is probably the same for the uninitated, but blocks are the core of Ruby so you get in contact with them within the first minutes of learning the language. And it's still shorter = easier to get a overview.

For books, I'd recommend "Programming Ruby" which can be ordered as PDF...
Left by Stoffe on Mar 28, 2007 7:13 AM

# re: Ruby/Rake and Python Decorators
Requesting Gravatar...
How is this:

@target('mytarget')
def MyFunction(target):
print "hello world", target.name
return True

Easier to read then this:

task :mytask do |t|
print 'hello world ' + t.name
end

I could give the latter to my wife and she could roughly guess each line much easier with no programming experience
Left by w on Aug 23, 2007 6:24 PM

# re: Ruby/Rake and Python Decorators
Requesting Gravatar...
I think you might be missing a critical feature of Python in that you can easily pass functions. So, if you have a list of tasks that you want to run you can add them directly to a list and run them:

<pre>
class DoOne(object):
def __call__(self, *args, **kw):
print "do one"

def my_function_action(*args, **kw):
print "do some func"

tasks = [DoOne(), my_function_action]
for t in tasks:
t()

</pre>

Also, the block-ish syntax can also be performed in list comprehensions if you really want something similar:

<pre>
result = [ t() for t in tasks ]
</pre>

Ruby's closures as blocks are nice, but it doesn't handle passing around functions very well, which is a deficiency IMHO. Ruby is still a great language and I'm still learning the ins and outs, but when compared to Python, it takes a slightly different route, while actually being very similar.
Left by Eric Larson on Jan 29, 2008 12:45 PM

Your comment:
 (will show your gravatar)


Copyright © Vagmi Mudumbai | Powered by: GeeksWithBlogs.net | Join free