Ruby、Pythonで動的ディスパッチ/動的メソッド定義

動的ディスパッチ

動的ディスパッチは、「実行時に呼び出すメソッドを決める」ことを指します。


rubyではsendによって動的ディスパッチができます

method_to_call = :upcase

obj = "abc"

obj.send(method_to_call)
# => "ABC"


pythonではメソッドがオブジェクトなので、getattrを使って、メソッドオブジェクトを取得して、実行することができます

method_to_call = 'upper'

obj = "abc"

getattr(obj, method_to_call)()
# => "ABC"

動的メソッド定義

動的メソッド定義は「実行時にメソッドを定義する」ことを指します。


rubyではdefine_methodを使用して動的にメソッドを定義することができます

以下は英語版wikipediarubyのページに記載されている例です

COLORS = { :black   => "000",
           :red     => "f00",
           :green   => "0f0",
           :yellow  => "ff0",
           :blue    => "00f",
           :magenta => "f0f",
           :cyan    => "0ff",
           :white   => "fff" }
 
class String
  COLORS.each do |color,code|
    define_method "in_#{color}" do
      "<span style=\"color: ##{code}\">#{self}</span>"
    end
  end
end

"Hello, World!".in_blue
=> "<span style=\"color: #00f\">Hello, World!</span>"

Stringにメソッドを動的に追加しています。define_methodを使うことによって、記述量を減らしています


pythonではメソッドがオブジェクトなので、setattrを使って、メソッドオブジェクトをクラスにセットすることができます
ただ、ビルトインクラスであるstrにメソッドを追加することはできませんので、strを継承したクラスを作成する必要があります

colours = {"black": "000",
           "red": "f00",
           "green": "0f0",
           "yellow": "ff0",
           "blue": "00f",
           "magenta": "f0f",
           "cyan": "0ff",
           "white": "fff"}

class MyString(str):
    pass

for name, code in colours.iteritems():
    def _in_colour(self, code=code):
        return '<span style="color: #%s">%s</span>' % (code, self)
    setattr(MyString, "in_" + name, _in_colour)

print MyString("Hello, world!").in_blue()
# => <span style="color: #00f">Hello, world!</span>