メタプログラミングパターン (1) 特異メソッド

Rubyによるデザインパターン』(ラス・オルセン著 ピアソン・エデュケーション刊)の例をPythonに変換して書いています。目次

メタプログラミングは動的言語の専売特許ではありません。静的なC++では、テンプレートやマクロを使ってメタプログラミングをします。それはつまり、メタプログラミング用に実行時とは別の言語を用意するということです。

しかし、動的言語のPythonならばより自然に柔軟な記述が可能です。コンパイル時と実行時の区別が無く、クラスやオブジェクトを、リストや辞書並みに、バリバリ変更できるので、メタプログラミング用にPython自身を使えるのです。

正直「パターン」と呼ぶには、メタプログラミングは多様すぎる気がしますが、
Rubyによるデザインパターン』では「メタプログラミングパターン」と呼んでいます。


Factoryパターンでは、オブジェクトを自前で作るのではなく、ファクトリに作らせる事で柔軟性・整備性を増していました。


以前作ったファクトリ関数は、決まったクラスのインスタンスを返していました。しかし、もっと柔軟にオブジェクトの性質を変更したい場合、特異メソッドを使う手もあります。


ここでは『植物』オブジェクトを返すnew_plant関数を作ります。

from singletonmethod import singletonmethod

class Plant(object):pass

def new_plant(stem_type, leaf_type):
    plant = Plant()
    
    @singletonmethod(plant)
    def stem(self):
        return stem_type
    
    @singletonmethod(plant)
    def leaf(self):
        return leaf_type
    
    return plant

if __name__ == "__main__":
    plant1 = new_plant("多肉質", "とげとげ")
    plant2 = new_plant("木質",   "丸い")
    
    print "植物1", plant1.stem(), plant1.leaf()
    print "植物2", plant2.stem(), plant2.leaf()
#singletonmethod.py

import new
def singletonmethod(obj):
    """
    >>> from singletonmethod import singletonmethod
    >>> class A(object):pass
    >>> a = A()
    >>> @singletonmethod(a)
    ... def hello(self):
    ...     print "hello world!"
    >>> a.hello()
    hello world!
    >>> type(a.hello)
    
    """
    def f(function):
        m = new.instancemethod(function, obj, type(obj)) 
        setattr(obj, function.__name__, m)
        return function
    return f

特異メソッドを使うと、オブジェクト毎に独自のメソッドを定義できます。上の例では、返す値が違うだけですが、その気になればいくらでも変なメソッドを定義する事ができます。


Rubyには特異メソッド用の構文がありますが、Pythonには特異メソッドそのものズバリが無いので、singletonmethod.pyで自作しています。

でも、構文が無いってことは、
Python界隈ではあまり推奨されていないってことなのかも・・・。