《12》クラスに頼らない OOP(6)〈Python 3.0 版〉

Previous| |Next
Python à la carte記事一覧
《12》クラスに頼らない OOP(6)

《著》小粒ちゃん《監修》小泉ひよ子とタマゴ倶楽部
第1版♪2003/05/25 ● 第2版♪2004/06/01 ● 第3版♪2009/02/28

関連記事

関数:高階関数として

高階関数〔higher-order function〕は、関数を引数/リターン値にできるので、その特徴を利用して実現します。

def push(self):
    def push_(item):
        self.items.append(item)
    return push_
def pop(self):
    def pop_():
        try:
            return self.items.pop()
        except:
            print("pop from empty stack")
    return pop_

関数 push/pop を定義するときに、仮引数 self を利用します。この self は、メソッド呼び出しに呼応するインスタンスそのものです。すると、この self を束縛した関数 push_/pop_ を、それぞれのリターン値に指定できます。

def Stack():
    class Spam: pass
    self = Spam(); del Spam

    self.items = []
    self.push = push(self)
    self.pop  = pop (self)
    return self

局所変数 self は、メソッド呼び出しに呼応するインスタンスを保持します。インスタンス属性 .push/.pop を設定するときには、メソッド呼び出し push/pop の実引数に self を指定します。すると、メソッド関数 .push/.pop を呼び出したときに、この self を介して、各インスタンスが保持する情報(データ)を操作できます。

《Note》同じ操作(アルゴリズム)を共有するとともに、メソッド呼び出しに呼応する各インスタンス(オブジェクト)を介して、固有の情報(データ)を適切に管理します。■

関数:クロージャーとして

クロージャー〔closure〕は、引数以外の変数を(実行時の環境ではなく)関数を定義した環境で解決するので、その特徴を利用して実現します。

def Stack():         # class Stack(object):
    class Spam: pass
    self = Spam(); del Spam

    def push(item):
        self.items.append(item)
    def pop():
        try:
            return self.items.pop()
        except:
            print("pop from empty stack")

    self.items = []
    self.push = push
    self.pop  = pop
    return self

局所変数 self は、メソッド呼び出しに呼応するインスタンスを保持します。その値は、関数 Stack を定義した環境で解決するので、関数 push/pop の本体を定義するときに self を利用できます。すると、メソッド関数 .push/.pop を呼び出したときに、この self を介して、各インスタンスが保持する情報(データ)を操作できます。

《Note》同じ操作(アルゴリズム)を共有するとともに、メソッド呼び出しに呼応する各インスタンス(オブジェクト)を介して、固有の情報(データ)を適切に管理します。■

def XStack():         # class XStack(Stack):
    self = Stack()

    def top():
        if self.items:
            return self.items[-1]
        return None

    self.top  = top
    return self

関数 XStack は、既存の Stack を拡張して、新たな機能 top を追加します。このように(クラスを模した)関数 Stack/XStack は、クラスを使って実現した Stack と良く似ているのが分かります。

クラス:関数 vs. 束縛されたメソッド

最後に、クラス版 Stack との違いを比較して、クラスに頼らない OOP の理解を深めます。

>>> Stack, hex(id(Stack))
(, '0x423ff0')

クラス Stack は「クラス」オブジェクトとして、固有の識別情報 0x423ff0 を持ちます。

    
メソッド pop は「関数」オブジェクトとして、固有の識別情報 0xff4f8 を持ちます。これは後に、複数のインスタンスから共有されます。
>>> s1 = Stack(); s1
<__main__.Stack object at 0xfddb0>
>>> s2 = Stack(); s2
<__main__.Stack object at 0xfdd10>
s1/s2 は、異なる「インスタンス」オブジェクトとして、それぞれ固有の識別情報 0xfddb0/0xfdd10 を持ちます。
    
<bound method Stack.pop of <__main__.Stack object at 0xfddb0>> <bound method Stack.pop of <__main__.Stack object at 0xfdd10>>
s1.pop/s2.pop は「メソッド」オブジェクトとして、各インスタンス s1/s2 に束縛されます。
>>> hex(id(s1.pop)); hex(id(s2.pop))
'0xdcad0'
'0xdcbc0'
メソッド s1.pop/s2.pop は、同じ関数 Stack.pop を共有するとともに、異なるインスタンス s1/s2 に束縛されるので、それぞれ固有の識別情報 0xdcad0/0xdcbc0 を持ちます。

Tips

異なるオブジェクトによって共有される操作(アルゴリズム)は、メソッド呼び出しに呼応する各インスタンス(オブジェクト)に束縛されることで、固有の情報(データ)を適切に管理できるようになります。
Last updated♪09/03/28