I came across an interesting post Metaprogramming: Ruby vs. Javascript, which discusses and contrasts about how metaprogramming can be implemented in Ruby and Javascript. I thought it might be fun to document the same from a python perspective as well.
Here are the discussed samples. All the ruby code is quoted from from the blog post linked to above whereas the python code is what I wrote.
1. Initial class declaration and initialisation
We first declare a Ninja class and create two instances.
Ruby
1 2 3 4 5 6 7 8 9 10 | class Ninja attr_accessor :name def initialize(name) @name = name end end drew = Ninja.new("Drew") adam = Ninja.new("Adam") |
Python
1 2 3 4 5 6 | class Ninja(object): def __init__(self,name) : self.name = name drew = Ninja('drew') adam = Ninja('adam') |
2. Add a method to the class
In this step we add a method to the class dynamically. It is therefore available to all the instances of the class
Ruby
1 2 3 4 5 6 7 8 9 10 | class Ninja def battle_cry puts "#{name} says zing!!!" end end drew.battle_cry # => Drew says zing!!! adam.battle_cry # => Adam says zing!!! |
Python
1 2 3 4 5 6 7 8 | def battle_cry(self): print '%s says zing!!!' % self.name Ninja.battle_cry = battle_cry drew.battle_cry() # => Drew says zing!!! adam.battle_cry() # => Adam says zing!!! |
3. Add a method to an instance
In this case we add a method only to a particular instance. The same method will not be available to other instances of the same class.
Ruby
1 2 3 4 5 6 | def drew.throw_star puts "throwing a star" end drew.throw_star # => throwing a star |
Python
1 2 3 4 5 6 7 | import types def throw_star(self): print 'throwing a star' drew.throw_star = types.MethodType(throw_star,drew) drew.throw_star() # => throwing a star |
4. Invoke a method dynamically
In this case we supply the method name as a string and invoke it.
Ruby
1 2 | drew.send(:battle_cry) # => Drew says zing!!! |
Python
1 2 | drew.__getattribute__('battle_cry')() # => Drew says zing!!! |
5. Defining class level methods dynamically with closures
Here a class level method is defined which closes over some of the attributes in its context (in this case the method color is able to access the variable color_name as a closure).
Ruby
1 2 3 4 5 6 7 8 9 10 | color_name = 'black' Ninja.send(:define_method, 'color') do puts "#{name}'s color is #{color_name}" end drew.color # => Drew's color is black adam.color # => Adam's color is black |
Python
1 2 3 4 5 6 7 8 9 10 | color_name = 'black' def color(self): print "%s's color is %s" % (self.name, color_name) Ninja.color = color drew.color() # => Drew's color is black adam.color() # => Adam's color is black |
6. Defining a method dynamically on an instance that closes over local scope and accesses the instance’s state
Note that in this case the function is being defined on the instance using a dynamic name (’swing’)
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class Object def metaclass class << self; self; end end end sword_symbol = "*********" drew.metaclass.send(:define_method, 'swing') do |sound_effect| puts "#{name}: #{sword_symbol} #{sound_effect}" end drew.swing 'slash!!' # => Drew: ********* slash!! |
Python
1 2 3 4 5 6 7 8 | sword_symbol = "*********" def foo(self,sound_effect): print "%s : %s %s" % (self.name,sword_symbol,sound_effect) drew.__dict__['swing'] = types.MethodType(foo,drew) drew.swing('slash!!') # => Drew : ********* slash!! |


That’s an interesting comparison. Could you contrast the ruby and python approaches in more detail? One thing that strikes me is namespace issues. It looks like your python code ends up defining functions in the global namespace and then assigning them to a class or instance, while the ruby code scopes the definitions more explicitly.
David,
The difference actually is due to the languages per se. python does not allow reopening of a class to add methods to it the way ruby allows.
The entry can be simply removed by using the del command as follows :
2
3
4
5
print '%s says zing!!!' % self.name
Ninja.battle_cry = battle_cry
# Remove the battle_cry entry
del battle_cry
Awesome post. Metaprogramming doesn’t seem to be used as much in the python world as it is in the ruby world, but it nice to see how to implement these patterns if need be.
[...] all the examples (in Perl 5 / Moose, Ruby, Javascript & Python) I think this one is the most clean and intuitive. Perl 6 has a good future if it keeps this [...]