/code/blog

Code, code and code

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
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!!

Comments