メタプログラミングruby2章のdynamic-dispathの例をpythonで

pythonでやってみました。なかなか綺麗にならない><

code

__init__より下にある関数は全部__init__のローカル関数。types.MethodTypeを使ってコンストラクタを実行時にインスタンスメソッドを生成。

## metaprograming ruby chapter2.
class Source:
    def get_cpu_info(self, id):
        return "cpu"
    def get_mouse_info(self, id):
        return "mouse"
    def get_keyboard_info(self, id):
        return "keyboard"
    def get_cpu_price(self, id):
        return 100
    def get_mouse_price(self, id):
        return 20
    def get_keyboard_price(self, id):
        return 20
    
class Computer:
    def __init__(self, computer_id, data_source):
        self.id = computer_id
        self.data_source = data_source

        import re
        rx = re.compile(r"^get_(.*)_info$")
        def _getter_iter():
            for name in data_source.__class__.__dict__.keys():
                m = rx.search(name)
                if m:
                    yield m.group(1)

        def _make_inspect(name):
            def _send(obj, mtd, *args):
                method = getattr(obj, mtd, None)
                if method is not None:
                    return method(*args)

            def _inspect(self, id):
                info =  _send(self.data_source,"get_%s_info" % name, self.id)
                price = _send(self.data_source,"get_%s_price" % name, self.id)
                result = "%s: %s (%s)" % (name.capitalize(), info, price)
                if price > 100:
                    return "* %s" % result
                return result
            import types
            return  types.MethodType(_inspect, self, Computer)
        
        for getter in _getter_iter():
            self.__dict__[getter] = _make_inspect(getter)

###########
data_source = Source()
com = Computer(1, data_source)
print com.keyboard(0)
print com.cpu(0)
print com.mouse(0)

output

Keyboard: keyboard (20)
Cpu: cpu (100)
Mouse: mouse (20)