質問: Pythonのコードについての質問です。 コード例) ・クラス定義 ``` class Orange: def __init__(self,w,c): self.weight=w self.color=c self.mold=0 print("Created!") def rot(self,days,temp): """temp(温度)は摂氏""" self.mold=days*temp ``` ・インスタンスの利用例 ``` orange=Orange(200,"orange") print(orange.mold) orange.rot(10,37) print(orange.mold) ``` 1__init__は、どういう働きをしているのか。 (初期化・・・といっても必要性がわかりません・・・) 2__init__の引数selfの必要性が参考書を見てもよくわからない。 (これは、次のインスタンス変数を定義する際のself変数とは別物ですよね?) 3試してみると__init__内で定義していない変数(試したのはコード5行目のmold)は、後で自分で定義したメソッド内(rot)で変数として使えなかったが、なぜ?(これはたぶん1がわかってないからだと思います) 以上3つが疑問点です。よろしくお願いいたします。
回答: ### `__init__` の必要性 オブジェクト指向プログラミングにおいて、あるオブジェクトのフィールドは、ある種の一環した値を取ることが期待されます。 Orange は、おそらく果物のオレンジをどのように管理したかによってどのような品質状態になるかをモデル化したクラスだと考えられます。なので、たとえば次のような使用例が考えられます。 Q. オレンジを 10 日間 30度 で、 その後に 5日 20度で保管した。黴具合はいくらか。 A. ``` class Orange: def __init__(self): self._mold=0 def rot(self,days,temp): """temp(温度)は摂氏""" self._mold += days*temp def getMold(self): return self._mold orange = Orange() orange.rot(10, 30) orange.rot(5, 20) print(orange.getMold()) # => 400 ``` このような使い方をした場合、 Orange クラスを使うユーザーは、 orange._mold に直接アクセス、特に代入することはしません。むしろ、それをやってしまうと、 Orange クラスがメソッドによって公開していた _mold フィールドに関する良い性質 (これまで呼ばれた rot に応じたかび度合い) が、壊れてしまいます。蛇足ですが、このようなユーザーが直接参照してほしくないフィールドは、 python ではアンダースコアから始まる変数によって初めることで示すという規約があります。 オブジェクト指向によってメンバーをまとめあげる大きなメリットの一つとして、このように、メンバー変数が満たしてほしい性質を、そのクラス設計者が想定する使い方すなわち、大体においてその公開されているメソッドを、呼び出していった場合において、常に満たすようにするということがあります。 オブジェクトを新しく作成したときに、 `__init__` に何も書かないと、それはクラスのフィールドが満たしてほしい性質を満たせません。自分が作成した例で言うならば、 「_mold はこれまで呼ばれた rot に応じた黴度合いを数値で表す」です。 (何も書かないと、 None になっていると思います。これだとなんか 0 と同じ感じではありますが、例えば、最初の黴度合いは 10 からスタートする、といったような性質が今扱いたい Orange モデルにあったとすれば、やはり初期化は欲しくなります。) ### self python では、すべてのメソッドはその第一引数に、そのメソッドを呼んでいる主体のオブジェクトを引数に取る、という言語設計です。たとえば ruby とかではこれは暗黙的に `self` にバインドされていますが、オブジェクト指向のメソッドの中であれば確実に存在しているはずの参照です。 ### 初期化 python において、 `__init__` は、「オブジェクトがメモリの上に作成されたときに、最初に必ず呼ばれるメソッド」であって、それ以上でも以下でもありません。 python は、言語設計により、クラスによって暗黙的に参照可能な変数というのはほとんど存在していないです。ほぼほぼ def でメソッドを定義したときにバインドされる変数たちぐらいです。 (たぶん。あんまり python くわしくないですが) python におけるインスタンス変数とは、 `オブジェクト.フィールド名称` で参照、 `オブジェクト.フィールド名称 =` で代入される、オブジェクトにひもづいたフィールドだけです。