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
class Ninja attr_accessor :name def initialize(name) @name = name end end drew = Ninja.new("Drew") adam = Ninja.new("Adam")
Python
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
class Ninja def battle_cry puts "#{name} says zing!!!" end end drew.battle_cry # => Drew says zing!!! adam.battle_cry # => Adam says zing!!!
Python
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
def drew.throw_star puts "throwing a star" end drew.throw_star # => throwing a star
Python
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
drew.send(:battle_cry) # => Drew says zing!!!
Python
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
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
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
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
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!!
Comments !