関数による簡易的なオブジェクトの作成

Pythonで関数によってクラス風味なモノを作りたい場合、クロージャと関数の名前空間を利用し以下のようにする。

def my_queue(L):
    L = L[:]
    idx = [0]
    def pop():
        try:
            v = L[idx[0]]
            idx[0] += 1
            return v
        except:
            return None
    def add(v):
        L.append(v)
    def top():
        try:
            return L[idx[0]]
        except:
            return None
    def repr():
        try:
            return L[idx[0]:]
        except:
            return None
    def f(): pass
    f.pop, f.add, f.top, f.repr = pop, add, top, repr
    '''
    f.L = L
    f.idx = idx
    '''
    return f

'''
def derived_queue(L):
    q = my_queue(L)
    def repr():
        return '*' + str(q.L[q.idx[0]:]) + '*'
    q.repr = repr
    return q
'''
    
if __name__ == '__main__':
    L = [3,4,5]
    q = my_queue(L)
    print 'q.repr() =', q.repr()
    q.add(1)
    q.add(2)
    print 'q.repr() =', q.repr()
    print 'q.top() =', q.top()
    print 'q.pop() =', q.pop()
    print 'q.repr() =', q.repr()
    print 'L =', L
    q2 = my_queue([7,8,9])
    q2.add(10)
    q2.add(11)
    print 'q2.repr() =', q2.repr()
    print 'q.repr() =', q.repr()   # q is not affected by another instance q2.
    '''
    q3 = derived_queue(range(6))
    q3.add(99)
    q3.pop()
    q3.pop()
    print 'q3.repr() =', q3.repr()
    '''

# 出力:
# q.repr() = [3, 4, 5]
# q.repr() = [3, 4, 5, 1, 2]
# q.top() = 3
# q.pop() = 3
# q.repr() = [4, 5, 1, 2]
# L = [3, 4, 5]
# q2.repr() = [7, 8, 9, 10, 11]
# q.repr() = [4, 5, 1, 2]

コメントを取り除けば、継承チックなことも可能。継承先で継承基のデータを使いたいなら、f.L = L や f.idx = idx のように公開しておかなければならない。実用性は分からないが、使い捨ての簡単なオブジェクトを作りたい場合は使えるかもしれない。


追記:
my_queue()のインスタンスを作るたびにpopやaddなどの関数のインスタンスも作成されると思うので、もちろんクラスを使った場合よりも効率は落ちると思う。クラスは極端なことを言えば「単なる名前空間によるカプセル化」と「インスタンスのマルチプル化」であり、継承は単なる関数のオーバーライドであると思うので機能的には大差はない。

しかし、手軽さを抜きにしてもクラスはメタクラスの存在、型としての意味合いを持てる、objectクラスによるデフォルト機能、演算子オーバーロード(ライド)など、プラスアルファな特殊な機能があるので、それ以上の機能を持つ。上記の2点の機能の合わせ技を実装することの出現頻度や重要性を考えて、それに利便性と効率を追求して機能にしたものがクラスであると思う。