というわけで、今日のネタはポリモーフィズムについてのメモです。というのも、以前にモナドを自分なりに実装してみたのですけれども、その実装をしているうちに、「あれ、これってポリモーフィズムを破壊していることになっているんじゃないか」と気がついたからでした。
ポリモーフィズムについては多態性と呼ばれています。『はじめてのPython』によれば、「1 + 1」は数字の追加になるわけだし、「"hoge" + "hoge"」ならば文字列の連結になります。このように、オブジェクトの種類によって、機能が変わってきます。そして、その関数が使えるかどうかは、決まった呼び出し方に対応しているかどうか、ということがポイントになってきます。
このあたりに関しては、抽象的に考えるより、実際にコードを書いてみるほうが早いと思うので、演算子のオーバーロードから、その当たりを考えてみたいなと思います。例えば、次のようなことをやってみましょう。
>>> class Hoge: ... pass ... >>> hoge = Hoge() >>> hoge + 1 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unsupported operand type(s) for +: 'instance' and 'int'
そりゃエラーが出るのは当たり前です。明らかに数字じゃないものを足しているわけだから。でも次のように書いてみるとどうでしょうか。
>>> class Hoge: ... def __add__(self,other): ... return "hoge" * other ... >>> hoge = Hoge() >>> hoge + 1 'hoge' >>> hoge + 3 'hogehogehoge'
なんと、オペレーターに対応しましたね。
そこで、Pythonのポリモーフィズムに直接触れるよりも、他の言語のポリモーフィズムについて調べてみましょう。例えば、『独習Java』には、明確に「一つのインターフェイスに複数の実装」というのをポリモーフィズムと述べています。例えば、こういうソースが掲載されているので、それを引用してみましょう。
abstract class Hoge{ abstract int returnSomething(); } class Fuga extends Hoge{ int returnSomething(){ return 4; } } class Poko extends Hoge{ int returnSomething(){ return 3; } }
さて、これがJavaにおけるポリモーフィズムの実例なのですが、これの何が嬉しいのか。というのも、ある変数が、どのクラスのインスタンスかわからないことが往々にしてあるからです。例えば、eseharaは、場合によってはnew Fugaかもしれないし、あるいはnew Pokoかもしれません。しかし、ポリモーフィズムを導入することによって、「別にどっちでもいいじゃん」と言えるわけです。実際に、次のようにも書けるわけです。
class PractiseJava2 { public static void main(String[] args) { Hoge esehara = new Fuga(); esehara = new Poko(); System.out.println(esehara.returnSomething()); } }
つまり、そのメソッドが呼び出せれば、どっちでもいいだろボケ、というのがポリモーフィズムなのかな、と思います。
さて、いきなりJavaの話を持ち出したのは他でもなく、『はじめてのPython』に、次のような話が出てくるからなのでした。
Pythonの関数にこのような特性があるということは、C++、Javaといった言語(引数、戻り値などの型があらかじめ固定される言語)とは、関数を作る際の発想をまったく変えなければならない、ということです。Pythonでは、特定の型を想定して関数のコードを書くべきではありません。もし特定の型を想定してコードを書けば、関数の応用範囲がそれだけ狭まることになります。
もちろん、型を用意するということは、予期しない入力などを最小限にしてくれるメリットはあるのですが、同時に柔軟性も失われます。特にPythonの場合だと、その型というより、型が持つインターフェイスのほうが重要だったりします。例えば、タプルとリストは型としては違うものですが、インターフェイスは一緒です。なので、下のように簡潔に書けます。
>>> a = (1,2,3) >>> b = ["a","b","c"] >>> for j in (a,b): ... x,y,z = j ... print x + y + z
極端に型をごちゃまぜにしてみましたが、無事処理してくれます。
さて、インターフェイスにこだわるという意味では、ダックタイピングという考え方があります。ダックタイピングとポリモーフィズム - みねこあの記事が参考になったので、ここを参考にしながら考えてみましょう。
ダックタイピングにおいては実行時に実際に呼ばれたメソッドのみが実装されていればよく、一方でテンプレートではコンパイル時にアンリーチャブルであると証明できなかった全てのメソッドを実装する必要があるため、ここでもダックタイピングはよりフレキシブルとなれる。
ポイントは、「呼ばれたメソッドが実装されていればよく」という部分でしょう。つまり、そのオブジェクトが「ところでお前は何ができるの?」ということにしか興味がなく、そのオブジェクト自体はどうでもいいわけです。「ガー」と鳴くんだったらカエルだろうがアヒルだろうがなんでもいいわけです。
>>> class Dack: ... def goo(self): ... return "Goo!!" ... >>> class Flog: ... def goo(self): ... return 100 >>> for Something in (Dack,Flog):print Something().goo() ... Goo!! 100
よく、目隠しをした人間が、象の身体を触って、各人が勝手な推測を立てている絵、というのがよく引用されていますが、むしろその全体像は特別知る必要はなく、そのメソッドがあるのかどうか、というところがポイントであると言えるでしょう。もちろん、あるときには文字列、あるときには数字というのはたまったものじゃないわけで、一長一短といったところなのかなという感じです。
Nathalia2012/02/27 16:47Begun, the great internet edcutaion has.
ohkolvvdzxr2012/02/28 04:23yzxKin <a href="http://hbxfkojebaew.com/">hbxfkojebaew</a>
rvrpcytgyrt2012/03/01 23:28U11XE4 <a href="http://tjohexuovvar.com/">tjohexuovvar</a>
sojwdyglf2012/03/14 11:02CZ6QQU , [url=http://vxenxweyawnz.com/]vxenxweyawnz[/url], [link=http://kyogbiuuwuxq.com/]kyogbiuuwuxq[/link], http://yupydfiovkjs.com/