Python

【Python】クラスの定義方法と使い方を解説

この記事では、Pythonのclass(クラス)について解説します。

クラスとは、つまりは「」のことです。クラスを定義することでint型やstr型と同じような便利な型を自ら作ることができます。

ちなみに、元から言語が用意してくれている型を「組み込み型」と呼び、ユーザーが定義した型のことを「ユーザー定義型」と呼びます。そのまんまですね!

型ってなんだっけ?

データの性質を定めて取り扱い方を定義したもの

それでは、クラスの使い方を見ていきましょう!

クラスの使い方

クラスは 以下のような手順で使用します。

  • STEP1
    クラスを定義する

    クラスは「classキーワード」を使って定義します。

    class クラス名:
        # 何かしらの処理

    クラス名の頭文字には大文字を使いましょう。また、複数の単語がくっついた名前を使用する場合は、単語の頭文字を大文字にします。例えば、MyClassみたいな感じです。

    クラスの中には「コンストラクタ」や「メソッド」を定義しますが詳しくは後述します。

  • STEP2
    インスタンス化

    クラスを使うには、そのクラスのインスタンスを生成する必要があります。

    インスタンスとは?

    クラスを元に生成したデータのこと

    インスタンスを生成するには、以下のようにクラスを呼び出します。

    インスタンス名 = クラス名()

    このようにクラスのインスタンスを生成することをインスタンス化と呼びます。1つのクラスからインスタンスはいくつでも生成することができます。

  • STEP3
    クラスの機能を呼び出す

    インスタンス化したことにより「インスタンス変数(クラスに定義されている変数)」「メソッド(クラスに定義されている関数)」を呼び出すことができます。

    インスタンス変数やメソッドを呼び出すには、インスタンス名と呼び出すインスタンス変数名やメソッド名を.(ドット)で繋いで呼び出します。

    インスタンス名.変数名
    
    インスタンス名.メソッド名()

    インスタンス変数とメソッドについては後述します。

以上がクラスを使う流れとなります。

インスタンスについて

クラスを使用するには、インスタンスを生成する必要があります。イメージとして、クラスは抽象的なもので、インスタンスはより具体的なものです。

例えば、Personという人のデータを扱うクラスを定義したとして、生成するインスタンスは、MikeJohnなどの具体的な人を生成します。

他にもPointという座標を扱うクラスを定義したとしたら、abなどの具体的な座標を持つインスタンスを生成して使います。

例として、座標を扱う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という座標を持つクラスから、abという具体的な値を持つインスタンスを生成することができました。

このように、抽象的なクラスから具体的な値を持つインスタンスを生成して使います。

コンストラクタ(イニシャライザ)

コンストラクタは、クラスをインスタンス化する際に呼び出されます。インスタンスを初期化する際の処理を定義できます。

定義

コンストラクタは、__init__という特殊メソッドを使って定義します。

class クラス名:
    # コンストラクタ
    def __init__(self):
        # なんらかの処理

以下のように引数を受け取ることもできます。

class クラス名:
    # コンストラクタ
    def __init__(self, 引数1, 引数2, ..., 引数N):
        # なんらかの処理

呼び出し

引数が定義されている場合は、インスタンス化する際に値を渡す必要があります。

# コンストラクタに引数が定義されていない場合
インスタンス名 = クラス名()

# コンストラクタに引数が定義されている場合はその分だけ値を渡す
インスタンス名 = クラス名(値1, 値2, ..., 値N)

サンプル

試しにコンストラクタを定義し、インスタンス化してみましょう!

class TestClass:
    # コンストラクタ
    def __init__(self):
        print('TestClassのコンストラクタが呼び出されました!')


# TestClassのインスタンス化
tc = TestClass()

実行結果

TestClassのコンストラクタが呼び出されました!

ちなみにコンストラクタは「イニシャライザ」とも呼ばれ、特に何も定義しないならば省略することもできます。

デストラクタ

デストラクタは、インスタンスが削除される際に呼び出されます。

定義

デストラクタは、__del__という特殊メソッドを使って定義します。

class クラス名:
    # デストラクタ
    def __del__(self):
        #何かしらの処理

サンプル

試しにデストラクタを定義して使ってみましょう!

class Test:
    # デストラクタ
    def __del__(self):
        print('デストラクタが呼び出されました!')


t = Test()

print('-'*30)
# del文でインスタンスを削除
del t
print('-'*30)

実行結果

------------------------------
デストラクタが呼び出されました!
------------------------------

このサンプルではdel文を使って明示的にインスタンスを削除しましたが、プログラム終了時にはインスタンスも削除されるのでデストラクタが呼び出されます。

selfとは?

先ほどのコンストラクタやデストラクタに定義されていたselfという引数があったかと思います。

selfは、自分自身(インスタンス)にアクセスするためのものです。クラスに定義されたコンストラクタやデストラクタを含めたメソッドは基本的にself引数を持ちます。

以下のコードでは、selfとインスタンスを出力しています。

class Test:

    def __init__(self):
        # selfの出力
        print(self)


test = Test()
# インスタンスの出力
print(test)

実行結果

<__main__.Test object at 0x1039994d0>
<__main__.Test object at 0x1039994d0>

selftestインスタンスは、まったく同じ値が出力がされたのでselfはインスタンスにアクセスしていることが確認できました!

インスタンスにアクセスすることでクラス内で自身の機能(インスタンス変数やメソッド)を使うことができます。

selfの使い方については都度で解説するので、ここでは「selfは自身のインスタンスにアクセスしてるんだ〜」という認識で構いません。

クラスに定義できる変数

クラスには、2種類の変数を定義することができます。

インスタンス変数

インスタンス変数は、インスタンス化しなければ呼び出せない変数です。インスタンスが持つ変数なのでインスタンスごとに異なる値を持つことができます。

定義

インスタンス変数を定義するには、コンストラクタ内でselfを用いて定義します。

class クラス名:
    def __init__(self):
        self.インスタンス変数名 = 初期値

もちろん、引数の値を渡すこともできます。

class クラス名:
    def __init__(self, 引数):
        self.インスタンス変数名 = 引数

呼び出し

インスタンス変数を呼び出すに以下のようにします。

インスタンス名.インスタンス変数名

動的な追加

インスタンス変数は、動的に追加することができます。

インスタンス名.追加する変数名 = 値

サンプル

試しにいくつかインスタンス変数を定義して出力してみましょう。

class Person:
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender


mike = Person('Mike', 14, '男')
print(f'名前: {mike.name}, 年齢: {mike.age}, 性別: {mike.gender}')

elizabeth = Person('Elizabeth', 36, '女')
print(f'名前: {elizabeth.name}, 年齢: {elizabeth.age}, 性別: {elizabeth.gender}')

実行結果

名前: Mike, 年齢: 14, 性別: 男
名前: Elizabeth, 年齢: 36, 性別: 女

クラス変数

クラス変数は、インスタンス化しなくても呼び出すことができる変数です。クラスが持つ変数なので異なるインスタンスでも共通の値を持ちます。

同名のクラス変数とインスタンス変数が定義されている場合、インスタンスからはインスタンス変数が呼び出されます。

定義

クラス変数を定義するには、クラス内で変数を定義します。

class クラス名:
    # クラス変数定義
    クラス変数名 = 値

呼び出し

クラス変数を呼び出すには、以下のように呼び出します。

クラス名.クラス変数名

動的な追加

クラス変数は、動的に追加することができます。

クラス名.追加する変数名 = 値

サンプル

試しにクラス変数を定義して呼び出してみましょう!

class Test:
    # クラス変数の定義
    var = 'クラス変数'


# クラス変数の呼び出し
print(Test.var)

実行結果

クラス変数
クラス変数はインスタンスからでも呼び出せますが、紛らわしいので極力クラスから呼び出すようにしましょう!

クラス内に定義した関数(メソッド)

クラス内に定義した関数のことを「メソッド」と呼びます。メソッドには、3つの種類があるので1つずつ見ていきましょう!

インスタンスメソッド

インスタンスメソッドは、インスタンス化しないと呼び出せないメソッドです。selfからインスタンス変数やインスタンスメソッドを呼び出すことができます。

定義

インスタンスメソッドは、selfを引数に持った関数を定義すれば良いだけです。

class クラス名:

    # 引数無し
    def メソッド名(self):
        # 何らかの処理

    # 引数有り
    def メソッド名(self, 引数1, 引数2, ..., 引数N):
        # 何らかの処理

呼び出し

インスタンスメソッドを呼び出すには、以下のようにします。

# 引数無し
インスタンス名.インスタンスメソッド名()

# 引数有り
インスタンス名.インスタンスメソッド名(値1, 値2, ..., 値N)

サンプル

試しにインスタンスメソッドを定義して使ってみましょう!

class Cat:

    # インスタンスメソッド
    def cry(self):
        print('にゃ〜')


tama = Cat()
tama.cry()

実行結果

にゃ〜

クラスメソッド

クラスメソッドは、インスタンス化しなくても呼び出せるメソッドです。
selfを使ったインスタンスへのアクセスはできません。

定義

クラスメソッドは、以下のように@classmethodデコレータを使って定義します。
引数には、clsを必ず定義する必要があります。

class クラス名:

    # 引数無し
    @classmethod
    def クラスメソッド名(cls):
        # 何らかの処理

    # 引数有り
    @classmethod
    def クラスメソッド名(cls, 引数名1, 引数名2, ..., 引数名N):
        # 何らかの処理

呼び出し

クラスメソッドを呼び出すには、以下のようにします。

# 引数無し
クラス名.クラスメソッド名()

# 引数有り
クラス名.クラスメソッド名(値1, 値2, ..., 値N)

サンプル

clsにアクセスすることで自らのクラスを呼び出すことができます。

class Point:
    # クラス変数
    num = 10

    def __init__(self, x, y):
        self.x = x
        self.y = y

    # クラスメソッド
    @classmethod
    def class_method(cls):
        # Point(1, Point.num)と同等
        return cls(1, cls.num)

    # 出力
    def output(self):
        print(f'x: {self.x}, y: {self.y}')


# クラスメソッドの呼び出し
p = Point.class_method()
p.output()
print(type(p))

実行結果

x: 1, y: 10
<class '__main__.Point'>

スタティックメソッド

スタティックメソッドは、インスタンス化しなくても呼び出せるメソッドです。
selfclsにはアクセスすることができません。

定義

スタティックメソッドは、以下のように@staticmethodデコレータを使って定義します。引数にselfclsは使えません。

class クラス名:

    # 引数無し
    @staticmethod
    def メソッド名():
        # 何らかの処理

    # 引数有り
    @staticmethod
    def メソッド名(引数名1, 引数名2, ..., 引数名N):
        # 何らかの処理

呼び出し

スタティックメソッドを呼び出すには、以下のようにします。

# 引数無し
クラス名.スタティックメソッド名()

# 引数有り
クラス名.スタティックメソッド名(値1, 値2, ..., 値N)

サンプル

試しにスタティックメソッドを定義して使ってみましょう!
通常の関数をクラス内に定義できるイメージです。

class Test:

    @staticmethod
    def s_method():
        print('スタティックメソッドです!')


# スタティックメソッドの呼び出し
Test.s_method()

実行結果

スタティックメソッドです!

属性(attribute)

属性とは、a.bbのことを言います。.で繋げて呼び出されるものは全て属性です。具体的に言えば、インスタンス変数やメソッドなどは属性です。

dir()を使うことでオブジェクトが持つ属性を確認することができます。

class Test:
    # クラス変数
    hoge = 'hoge'

    def __init__(self):
         # インスタンス変数
         self.moge = 'moge'

    # インスタンスメソッド
    def method(self):
         print(self.moge)


# クラスの属性の確認
print(dir(Test))

# インスタンスの属性の確認
t = Test()
print(dir(t))

実行結果

['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'hoge', 'method']
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'hoge', 'method', 'moge']

大量にある'__メソッド名__'は、特殊メソッドと呼ばれるメソッドです。コンストラクタやデストラクタもこれに含まれます。

実行結果を右にスクロールして最後の方を見て下さい。
どちらの出力結果にもTestクラスの属性が追加されているのがわかります。

mogeは、インスタンス変数なのでインスタンス化しないと生成されません。

特殊メソッドには、演算子をオーバーロードできるものも用意されています。詳しくは以下の記事を参照してください。

演算子をオーバーロードする特殊メソッドの使い方

継承

クラスは継承することができます。

継承とは「あるクラスから派生したクラスを定義すること」を言います。継承させることで親の性質や機能を引き継ぎ、さらに拡張・追加することができます。

例えば、文字列を扱いたいけど通常の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のクラスについて解説しました。

今回は覚える内容が多かったと思いますが、オブジェクト指向言語にとってクラスはとても重要な機能になります。何度も読み返してしっかりと覚えましょう!

それでは今回の内容はここまでです!ではまたどこかで〜( ・∀・)ノ