Fork me on GitHub

Dynamically adding methods with metaprogramming : Ruby and Python

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 !

social