この記事では、Pythonのクラス(class)の定義と使い方を解説します。
クラスとは、「型(オブジェクト)」を定義するための機能です。 クラスを用いることでデータ(属性)と機能(メソッド)を一緒にまとめることができ、抽象的な概念を定義することができます。
ちなみに、あらかじめ用意されている型のことを「組み込み型」と呼び、ユーザーが定義した型のことを「ユーザー定義型」と呼びます。そのまんまですね!
それでは、クラスの使い方を見ていきましょう!
クラスを使用する手順
クラスは以下のような手順で使用します。
-
STEP1クラスを定義する
クラスは「
class
キーワード」を使って定義します。class クラス名: # 何かしらの処理
クラス名の頭文字には大文字を使いましょう。また、複数の単語がくっついた名前を使用する場合は、単語の頭文字を大文字にします。例えば、
MyClass
みたいな感じです。クラスの中には「コンストラクタ」や「メソッド」を定義しますが詳しくは後述します。
-
STEP2インスタンス化
クラスを使用するには、そのクラスのインスタンスを生成する必要があります。
インスタンスとは?クラスを元に生成したデータのこと
インスタンスを生成するには、以下のようにクラスを呼び出します。
インスタンス名 = クラス名()
このようにクラスのインスタンスを生成することをインスタンス化と呼びます。1つのクラスからインスタンスはいくつでも生成することができます。
-
STEP3クラスの機能を呼び出す
インスタンス化したことにより「インスタンス変数(クラスに定義されている変数)」や「メソッド(クラスに定義されている関数)」を呼び出すことができます。
インスタンス変数やメソッドを呼び出すには、インスタンス名と呼び出すインスタンス変数名やメソッド名を
.(ドット)
で繋いで呼び出します。インスタンス名.変数名 インスタンス名.メソッド名()
インスタンス変数とメソッドについては後述します。
以上がクラスを使用する基本的な手順となります。
インスタンスについて
クラスを使用するには、インスタンスを生成する必要があります。イメージとして、クラスは抽象的なもので、インスタンスはより具体的なものです。
例えば、Person
という人のデータを扱うクラスを定義したとします。そのクラスから生成するインスタンスは、Mike
やJohn
などの具体的な人を生成したりします。
他にもPoint
という座標を扱うクラスを定義したとしたら、a
やb
などの具体的な座標を持つインスタンスを生成して使います。例として、座標を扱うPoint
クラス定義して使ってみる :
# 座標を扱うクラス
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
# インスタンス化
a = Point(1, 2)
b = Point(4, 0)
print(a.x, a.y) # 1 2
print(b.x, b.y) # 4 0
Point
という座標に見立てた変数を持つクラスから、a
とb
という具体的な値を持つインスタンスを生成することができました。
このように、抽象的なクラスから具体的な値を持つインスタンスを生成して使います。
コンストラクタ(イニシャライザ)
コンストラクタ(イニシャライザと呼ばれることもある)は、クラスをインスタンス化する際に呼び出されるメソッドです。__init__
という特殊メソッドを用いて定義できます。
class クラス名:
# コンストラクタ
def __init__(self):
# なんらかの処理
以下のように引数を受け取ることもできます。
class クラス名:
# コンストラクタ
def __init__(self, 引数1, 引数2, ..., 引数N):
# なんらかの処理
引数が定義されている場合は、インスタンス化する際に必要な数だけ値を渡す必要があります。
インスタンス名 = クラス名(値1, 値2, ..., 値N)
例としてクラスにコンストラクタを定義し、インスタンス化してみる :
class TestClass:
def __init__(self, num, text):
print(num, text)
tc = TestClass(819, 'コンストラクタ')
# 819 コンストラクタ
上記コードでは、受け取った2つの引数をprint()
関数で出力するコンストラクタを定義しました。インスタンス化されると渡された値がコマンドラインに出力されます。
ちなみにコンストラクタは省略することもできます。
デストラクタ
デストラクタは、インスタンスが削除される際に呼び出されるメソッドです。__del__
という特殊メソッドを用いて定義できます。
class クラス名:
# デストラクタ
def __del__(self):
#何かしらの処理
例としてデストラクタを定義したクラスをインスタンス化し、del
文で明示的に削除してみる :
class Test:
# デストラクタ
def __del__(self):
print('デストラクタが呼び出されました!')
t = Test()
del t # デストラクタが呼び出されました!
上記コードでは、del
文を使って明示的にインスタンスを削除しましたが、プログラム終了時にはインスタンスも削除されるのでデストラクタが呼び出されます。
selfとは?
先ほど解説したコンストラクタやデストラクタの引数にはself
というものが記述されていました。self
は、自分自身(インスタンス)にアクセスするためのものです。
クラスに定義されたメソッドは基本的にself
引数を持ちます。自身のインスタンスにアクセスすることでクラス内から自分の機能(インスタンス変数やメソッド)を使うことができます。
self
の使い方については必要になる度に解説していくので、ここでは「selfは自身のインスタンスにアクセスしてるんだ〜」という認識で構いません。
クラスに定義できる変数
クラスには、「インスタンス変数」と「クラス変数」という2種類の変数を定義することができます。
インスタンス変数
インスタンス変数は、インスタンス化しなければ呼び出せない変数です。 インスタンスが持つ変数なのでインスタンスごとに異なる値を持つことができます。
インスタンス変数を定義するには、コンストラクタ内でself
を用いて定義します。
class クラス名:
def __init__(self):
self.インスタンス変数名 = 初期値
もちろん、引数で初期化することもできます。
class クラス名:
def __init__(self, 引数):
self.インスタンス変数名 = 引数
インスタンス変数にアクセスするには、以下のようにインスタンス名と呼び出したいインスタンス変数名をドット(.
)で繋げて記述します。
インスタンス名.インスタンス変数名
また、インスタンス変数は動的に追加することもできます。
インスタンス名.追加するインスタンス変数名 = 値
例として、半径(radius
)をインスタンス変数として持つ円(circle
)クラスを定義し、インスタンス化してから半径を出力してみます :
class circle():
def __init__(self, radius):
# 半径を表すインスタンス変数
self.radius = radius
# 半径に5を渡してインスタンス化
c = circle(5)
# 半径にアクセス
print(c.radius) # 5
上記コードでは、円クラスに半径というデータを持たせましたが、長さは円(インスタンス)によって変えられるようにインスタンス変数で定義しました。このように、インスタンスごとに異なる値を持たせたい場合はインスタンス変数を用います。
クラス変数
クラス変数は、インスタンス化しなくても呼び出すことができる変数です。 クラスが持つ変数なので異なるインスタンスでも共通の値を持ちます。
クラス変数を定義するには、メソッド以外のクラス内で変数を定義します。
class クラス名:
クラス変数名 = 値
クラス変数にアクセスするには、以下のようにクラス名と呼び出したいクラス変数名をドット(.
)で繋げて記述します。
クラス名.クラス変数名
クラス変数もインスタンス変数と同様に動的に追加することができます。異なるのは呼び出し元がクラス名ということです。
クラス名.追加する変数名 = 値
例として、円クラスに円周率(pi)のクラス変数を定義します :
class circle():
# 円周率のクラス変数
pi = 3.1415
def __init__(self, radius):
self.radius = radius
# 円周率にアクセス
print(circle.pi) # 3.1415
上記コードでは、円周率はどの円でも共通なのでクラス変数として定義しました。このように、クラス変数にはクラスで共通の値を持たせることができます。
- クラス変数はインスタンスから呼び出すことも可能ですが、同名のインスタンス変数が定義されている場合、インスタンス変数が優先して呼び出されてしまう
クラス内に定義した関数(メソッド)
クラス内に定義した関数のことを「メソッド」と呼びます。メソッドには、「インスタンスメソッド」、「クラスメソッド」、「スタティックメソッド」の3つの種類があるので1つずつ見ていきましょう!
インスタンスメソッド
インスタンスメソッドは、インスタンス化しないと呼び出せないメソッドです。
self
からインスタンスにアクセスすることができる
インスタンスメソッドを定義するには、self
を第一引数に持った関数をクラス内に記述します。
class クラス名:
# 引数無し
def メソッド名(self):
# 何らかの処理
# 引数有り
def メソッド名(self, 引数1, 引数2, ..., 引数N):
# 何らかの処理
インスタンスメソッドにアクセスするには、以下のようにインスタンスと呼び出したいメソッドをドット(.
)で繋げて記述します。
# 引数無し
インスタンス名.インスタンスメソッド名()
# 引数有り
インスタンス名.インスタンスメソッド名(値1, 値2, ..., 値N)
例として円クラスに面積を返すインスタンスメソッドを定義してみます :
class circle():
pi = 3.1415
def __init__(self, radius):
self.radius = radius
# 今回定義したインスタンスメソッド
def area(self):
return self.radius ** 2 * self.pi
c = circle(5)
print(c.area()) # 78.53750000000001
上記コードでは、インスタンス変数である半径とクラス変数である円周率を使って面積を求めるインスタンスメソッドarea
を定義しました。このように、メソッド内で自身のデータや機能にアクセスしたい場合にはインスタンスメソッドを使います。
クラスメソッド
クラスメソッドは、インスタンス化しなくても呼び出せるメソッドです。
self
を使ったインスタンスへのアクセスはできないcls
から自身のクラスにアクセスできる- 主に特定のデータを持つインスタンスを生成する際に使用される
クラスメソッドは、以下のように@classmethod
デコレータをメソッドに修飾させることで定義することができます。また、第一引数には、cls
を必ず記述する必要があります。
class クラス名:
# 引数無し
@classmethod
def クラスメソッド名(cls):
# 何らかの処理
# 引数有り
@classmethod
def クラスメソッド名(cls, 引数名1, 引数名2, ..., 引数名N):
# 何らかの処理
クラスメソッドにアクセスするには、以下のようにクラスと呼び出したいメソッドをドット(.
)で繋げて記述します。
# 引数無し
クラス名.クラスメソッド名()
# 引数有り
クラス名.クラスメソッド名(値1, 値2, ..., 値N)
例として円クラスに半径がランダムで設定されたインスタンスを生成するクラスメソッドを定義してみる :
from random import randint
class circle():
pi = 3.1415
def __init__(self, radius):
self.radius = radius
def area(self):
return self.radius ** 2 * self.pi
# 今回定義したクラスメソッド
@classmethod
def rand_instance_generator(cls, min, max):
# circle(randint(min, max)) と同じ
return cls(randint(min, max))
c = circle.rand_instance_generator(1, 100)
print(c.radius) # 29
上記コードでは、random
モジュールのrandint
関数を使ってクラスメソッドで生成するインスタンスの半径をランダムに設定しています。このように、クラスメソッドではクラスの機能を使用したメソッドを定義することができます。
スタティックメソッド
スタティックメソッドは、インスタンス化しなくても呼び出せるメソッドです。
self
やcls
は使えない
スタティックメソッドは、以下のように@staticmethod
デコレータをメソッドに修飾させることで定義することができます。
class クラス名:
# 引数無し
@staticmethod
def メソッド名():
# 何らかの処理
# 引数有り
@staticmethod
def メソッド名(引数名1, 引数名2, ..., 引数名N):
# 何らかの処理
スタティックメソッドにアクセスするには、以下のようにクラスと呼び出したいメソッドをドット(.
)で繋げて記述します。
# 引数無し
クラス名.スタティックメソッド名()
# 引数有り
クラス名.スタティックメソッド名(値1, 値2, ..., 値N)
例として円クラスに外部から半径を受け取って面積を求めるスタティックメソッドを定義する :
from random import randint
class circle():
pi = 3.1415
def __init__(self, radius):
self.radius = radius
def area(self):
return self.radius ** 2 * self.pi
@classmethod
def rand_instance_generator(cls, min, max):
return cls(randint(min, max))
# 今回定義したスタティックメソッド
@staticmethod
def calculation_area(radius):
return radius ** 2 * circle.pi
print(circle.calculation_area(4)) # 50.264
スタティックメソッドは、そのクラス内でしか使用されないメソッドやそのクラスに関係はしているけどインスタンスにはアクセスしないメソッドなどを定義する際に使用されます。
属性(attribute)と特殊メソッド
属性とは、a.b
のb
のことを言います。.
で繋げて呼び出されるものは全て属性です。dir()
を使うことでオブジェクトが持つ属性を確認することができます。
from random import randint
class circle():
pi = 3.1415
def __init__(self, radius):
self.radius = radius
def area(self):
return self.radius ** 2 * self.pi
@classmethod
def rand_instance_generator(cls, min, max):
return cls(randint(min, max))
@staticmethod
def calculation_area(radius):
return radius ** 2 * circle.pi
# クラスが持つ属性の確認
print(dir(circle))
# インスタンスが持つ属性の確認
c = circle(100)
print(dir(c))
実行結果
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'area', 'calculation_area', 'pi', 'rand_instance_generator']
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'area', 'calculation_area', 'pi', 'radius', 'rand_instance_generator']
大量に出力された'__メソッド名__'
は、「特殊メソッド」と呼ばれるメソッドです。コンストラクタやデストラクタもこれに含まれます。
特殊メソッドには、演算子をオーバーロードできるものやクラス自体の振る舞いを設定できるものも用意されています。詳しくは以下の記事を参照してください。
継承
クラスは継承することができます。継承とは、あるクラスから派生したクラスを定義することを言います。 継承させることで親の性質や機能を引き継ぎ、さらに拡張・追加することができます。
例えば、文字列を扱いたいけど通常のstr
型では物足りないなっと思った時に、str
型を継承した新しいクラスを定義し、自分が欲しい機能を追加したりできます。
# クラス名の後ろに(継承する型名)を記述する
class MyStr(str):
def 追加するメソッド(self):
print('追加メソッド')
ms = MyStr('ABC')
ms.追加するメソッド()
# もちろん、strのメソッドも使用可能
ms.index('B')
実行結果
追加メソッド
str
型に定義されているメソッドを拡張・変更することも可能です。詳しくは以下の記事を参照してください。
合成と委譲
継承は使いこなすのが難しいので代わりに「合成と委譲」を使うことでより簡単で安全に同じような処理を実装することができます。
合成とは、使用したい機能を持つオブジェクトを属性として持たせることを言います。例えば、文字列のような機能を使いたいと思ったら文字列を属性に持たせます。
class MyStr():
def __init__(self, s: str) -> None:
self._s = s
移譲とは、合成で持たせた属性をメソッドから呼び出すことを言います。
class MyStr():
def __init__(self, s: str):
self._s = s
# 委譲
def count(self, char):
return self._s.count(char)
ms = MyStr('ABCA')
print(ms.count('A'))
実行結果
2
継承を使わずとも合成と委譲を使うことで同様の処理を実装できました。詳しくは以下の記事を参照してください。
まとめ
この記事では、Pythonのクラスについて解説しました。
クラスはPythonの中心的な概念であり、オブジェクト指向プログラミングにとって重要な機能となります。何度も読み返してしっかり覚えておきましょう。
それでは今回の内容はここまでです!ではまたどこかで〜( ・∀・)ノ