《10》クラスに頼らない OOP(4)〈Python 3.0 版〉
《Previous| |Next》
Python à la carte《記事一覧》
《10》クラスに頼らない OOP(4)
関連記事
- C言語で始める OOP, 1988, 1994.
- 実録《14》クラス(0)要求仕様《Python3.1》 - 続・ひよ子のきもち〈Python 3.0 版〉
なぜクラスが必要なのか
クラスに頼らなくても、インスタンスオブジェクトに固有の状態を保持したり(クラス継承に代えて)機能を拡張/変更できるのを実証しました。では、なぜクラスが必要なのでしょう。その理由を明らかにする前に、ここまでの例題の問題点を解消しておきます。
クラス継承に代えて
前述した例題 ex4/ex5 では、インスタンスオブジェクトを生成した後で、個別に機能を追加(拡張/変更)できるという利点があります。しかし〈例題 ex3〉のように、同じ機能を持つ複数のインスタンスが必要になる状況では、逆にそれが欠点になります。なぜなら、新たなインスタンスを生成するたびに、既存の(継承した)機能を追加する必要があるからです。
この問題を解決するには、共通する機能を再利用するとともに、個別の問題解決にも対処できるような工夫が必要になります。その選択肢のひとつとして、差分プログラミングを支援する「クラス」が注目されたわけです。しかし、それを実現するだけなら、クラスに頼らなくても可能です。
■ 機能の拡張
前述した〈例題 ex4〉では、新たな機能 top を追加するのに、生成したばかりのインスタンスに新たな属性を設定しました。しかし、そのたびに同じ設定をするのは面倒です。そこで、
def XStack(): # class XStack(Stack): self = Stack() self.top = lambda: top(self) return self
関数 Stack を再利用するとともに、その機能を「拡張」するのに、インスタンス属性 .top に(クロージャーとしての)lambda 関数を設定します。self の値は、lambda 関数を定義するときに解決するので、XStack のインスタンスに固有の関数(メソッド)になります。すると、次のように、
def ex6(): stack = XStack() ;print(stack) stack.push("A") ;print(stack) s = stack.top() ;print(s) stack.push("B") ;print(stack) s = stack.top() ;print(s) s = stack.pop() ;print(stack,s) s = stack.top() ;print(s)
追加した関数 top を利用して、
>>> ex6()
[]
['A']
A
['A', 'B']
B
['A'] B
A
スタックの最上位にある値を参照できるのが分かります。
■ 機能の変更
前述した〈例題 ex5〉では、既存の機能 top を変更するのに、生成したばかりのインスタンスに新たな属性を設定しました。やはり、そのたびに同じ設定をするのは面倒です。そこで、
def IntStack(): # class IntStack(Stack): self = Stack() self.push = lambda item: push_int(self,item) return self
関数 Stack を再利用するとともに、その機能を「変更」するのに、インスタンス属性 .push に(クロージャーとしての)lambda 関数を設定します。self の値は、lambda 関数を定義するときに解決するので、IntStack のインスタンスに固有の関数(メソッド)になります。すると、次のように、
def ex7(): stack = IntStack() ;print(stack) stack.push(1) ;print(stack) stack.push("2") ;print(stack) stack.push(2) ;print(stack)
変更した関数 push を利用して、
>>> ex7()
[]
[1]
stack expected 'int' item, got '2'
[1]
[1, 2]
スタックには整数以外の値を積めなくなるのが分かります。
Tips
共通する関数 Stack をもとにして、その機能を拡張した XStack や、その機能を変更した IntStack など、クラスによって支援される「差分プログラミング」の特徴は、クラスに頼らなくても実現できます。