Pythonに限らず「クラス(class)」は、オブジェクト指向プログラミングにおいて必須の書き方です。
しかし、そもそもオブジェクト指向とは何なのか、クラスでは何ができるのかという点について、あまり理解が進んでいないという方もいるのではないでしょうか。
そこで、この記事では、オブジェクト指向プログラミングにおいて重要な、クラスの使い方(クラスの作り方、コンストラクタの書き方、メソッドの書き方、継承など)について、出来るだけ平易に解説していきます。
当記事が、あなたの役に立てば嬉しく思います。
「オブジェクト指向プログラミングの概念と書き方」では、オブジェクト指向以前のプログラミングのスタイルと比較しながら、オブジェクト指向とは何かについて詳しく解説しています。ぜひ、当記事とセットでご覧ください。
1. クラスの作り方
Pythonでは、クラスは次のような構文で作ります。
class クラス名():
クラスの定義
まずclassと書いて、その後にクラス名を書きます。
Pythonのクラス名は、常に先頭を大文字にするというのがルールです。もし、クラス名が複数の単語からなる場合はCircleCircleのように、全ての単語の最初は大文字で書きます。
以下のコードではCircleというクラスを作っています。
class Circle():
pass # 何も定義しない時はpassと書きます。
これで、まだ中身は何も定義していませんが、Circleというクラスが作られました。
このクラスのインスタンスを作るには、クラス名()と書きます。以下のコードでは、Circleクラスのインスタンスを変数cir1に代入しています。
なお、インスタンスとは何かについてはこの後解説します。ここでは、インスタンスは、文字列型オブジェクトやリスト型オブジェクトのようなオブジェクトだとお考え頂いて決して間違いではありません。
cir1 = Circle()
print(type(cir1))
type()関数で型を確認すると、このインスタンスのデータ型はCircle型であることが分かります。
2. コンストラクタ(特殊メソッド)の書き方
それでは、ここからクラスを定義していきましょう。
まずは、コンストラクタ(別名「特殊メソッド」)の定義です。
コンストラクタとは、「__init__」という名前で始まる部分のことをいいます。これによって、このクラスに、どのようなデータ(属性)を持たせたいのかを定義することができます。
class クラス():
def __init__(self, 引数): # ←コンストラクタ
属性定義
前後に2つずつ続くアンダーバーは必須です。また第一引数には、必ずselfを渡します。
defで始まっていることからも分かる通り、属性の定義は、「Pythonのdef文で関数を作る方法」で解説している、関数を作る時のdef文と同じように行います。
以下のコードでは、Circleクラスに、radius(=半径)という属性を定義しています。
class Circle():
def __init__(self, radius):
self.r = radius
さて、これでradiusという属性を持つCircleクラスが完成しました。
なお属性定義の中で「self.r=radius」と書いています。「self.r」は「インスタンス変数」と言います。手前についているselfは、それぞれのインスタンス自身を意味します。
このことを理解するために、先に二つのインスタンスを作ってみましょう。
Circleクラスは、radiusという属性を持つようになったので、このクラスのインスタンスを作る時に、次のように、必ず、radiusの値を引数に渡す必要があります。ここでは、radiusが10と15の二つのインスタンスを作っています。
cir1 = Circle(10)
cir2 = Circle(15)
インスタンスを作成した後は、次のようにしてインスタンス変数の値を取得することができます。
print(cir1.r)
print(cir2.r)
先ほど、属性定義の時に、「self.r = radius」の「self」はインスタンス自身のことだとお伝えしました。これは、インスタンスcir1にとっては「cir1.r」、インスタンスcir2にとっては「cir2.r」と同義だという意味です。
この理解は、次のインスタンスメソッドの定義で役立ちます。
【解説】インスタンスとは
オブジェクト指向プログラミングでは、「オブジェクト」や「インスタンス」という言葉が出てきます。どちらも似ている概念で、最初は混乱すると思いますので解説しておきます。
まず、インスタンスは「実体」という意味です。
例えば、ここではCircleクラスを作り、特殊メソッドで、Circleクラスにはradius(=半径)という属性があると定義しました。そして、半径が10のcir1と、半径が15のcir2を作りました。これによって、cir1とcir2は半径という実体ができたので、これらはCircleクラスのインスタンスです。
一方、オブジェクトは、「もの」という意味です。
これには広い意味があり、クラスもインスタンスもオブジェクトです。クラスは属性を定義している「もの」であり、インスタンスは属性の値を持って実体化した「もの」だからです。
そのため、’文字列’を「文字列型オブジェクト」といったり、[1, 2, 3]を「リスト型オブジェクト」といったりしますが、それらは正確には、strクラスのインスタンスであり、listクラスのインスタンスです。ただし、インスタンスはオブジェクトの一つなので、オブジェクトと呼んでも、もちろん論理的に正しいです。
3. インスタンスメソッドの書き方
続いて、インスタンスメソッド(=このクラスのインスタンスが使えるメソッド)を作ります。
クラスメソッドも、classブロックの中でdef文で定義します。
class クラス名:
def メソッド名(self, 引数):
メソッド定義文
第一引数に、それぞれのインスタンス自身を示すselfを入れる必要があること以外は、関数と全く同じように作ることができます(参照:「Pythonのdef文で関数を作る方法」)。
以下のコードでは、円の面積を求めるarea()メソッドと、円の外周の長さを求めるaround()メソッドを作っています。
class Circle():
def __init__(self, radius):
self.r = radius
def area(self):
area = self.r * self.r * 3.14
return f'{area:.2f}'
def around(self):
round = self.r * 2 * 3.14
return f'{round:.2f}'
メソッドの戻り値は、f文字列で、小数点以下2位まで表示するようにしています(参照:「Pythonのformat()メソッドの使い方」、「Pythonの文字列の中で変数を展開する方法」)。
それでは、次の2つのインスタンスを作って、それぞれのメソッドを呼び出してみましょう。
cir1 = Circle(10)
cir2 = Circle(5)
まずは、cir1にメソッドを使います。
print(cir1.area())
print(cir1.around())
半径が10なので、面積は314.00、外周の長さは62.80ですね。
半径が5のcir2に、同様のメソッドを使うと結果は次の通りです。
print(cir2.area())
print(cir2.around())
今回は、2つとも引数を必要としないメソッドですが、もちろん、関数と同じように必須引数や可変長の引数を指定することも可能です。書き方は、第一引数にselfを書く必要がある点を除いて、関数を作るときと同じです。
4. 継承
「継承」とは、既存のクラスを引き継いで、別のクラスを作ることをいいます。
親クラスを継承して作った子クラスでは、親クラスの特殊メソッドやインスタンスメソッドをそのまま使うことができます。
次のように書きます。
class 子クラス名(親クラス名):
以下のコードではCircleクラスを継承してSubCircleクラスを作り、特殊メソッドを上書きし、円錐の体積を求めるインスタンスメソッドvolume()を追加しています。
class SubCircle(Circle): # ←Circleクラスを継承
def __init__(self, radius, height): # ←特殊メソッドを上書き
self.r = radius
self.h = height
def volume(self): # ←インスタンスメソッドを追加
volume = 1/3 * self.r * self.r * 3.14 * self.h
return f'{volume:.2f}'
特殊メソッドを上書きして、属性にheight(=高さ)を追加しているので、SubCircleクラスのインスタンスを作るには、引数を2つ指定します。
cir3 = SubCircle(5, 10)
メソッドは、親クラスで定義されているものはそのまま使えますし、子クラスで新たに定義した円錐の体積を求めるメソッドも使えます。
print(cir3.area()) # ←親クラスで定義されているメソッド
print(cir3.around()) # ←親クラスで定義されているメソッド
print(cir3.volume()) # ←子クラスで定義したメソッド
なお子クラスで上書きしたり、新たに追加したりしたメソッドは親クラスには影響しません。
5. まとめ
それではPythonのクラスの重要ポイントをまとめましょう。
- classから始まるブロックでクラスの属性と、そのクラスのインスタンスが使えるメソッドを定義する。
- 「def __init__(self, 引数):」から始まるコンストラクタ(特殊メソッド)で、クラスの属性(=インスタンスの変数)を定義する。この時、第一引数には必ずselfを書く。
- インスタンスメソッドもdef文で関数と同じように定義する。この時も、第一引数は必ずselfを書く。
- 継承といって、一度作ったクラスのコンストラクタやインスタンスメソッドを引き継いだ子クラスを簡単に作ることができる。
これらの内容を参考に、実際に、様々なクラスを作ってみましょう。
コメント
コメント一覧 (6件)
python初心者でクラスの概念がなかなかピンとこなかったのですが、このエントリを拝見し、腑に落ちました。特に、クラス内のインスタンス定義やインスタンスメソッドの説明がとてもわかりやすかったです。ありがとうございます!
selfが毎回いるのはこのクラスを呼び出すときに、もう1回新しく初期化して前回の挙動を引き継がないようにするためという理解でいいんでしょうか。
あと、メソッドと関数の違いがよくわかりませんでした。
本を読んでも良くわからなかったことがこんなにわかりやすく解説されていて本当にありがたいです。今までで一番丁寧でわかりやすい。
より実戦的で、イメージを固めやすい解説でした。他の記事もとても参考になります。
わかりやすすぎる。
最初勉強したのはアメリカのEric Matthe書いた中文版【PythonCrash Course】、そもそもこの本は初心者にわかりやすい本です。でもその中に全てをわかるではない、ある知識がわかるでも使えなくて。わからない物はずっとわからなくてこれ以上に進められない。間違いやすいところがパタンで解説されていて本当に感心してます。いろんなPython勉強サイトを見た上でこのサイトは外国人にとってもわかりやすい素晴らしいサイトです!本当にありがたいです!