クラス

【Python】クラスの定義と使い方

【Python】クラスの定義と使い方

Pythonでクラスを定義する方法及び使い方について基本的な部分を解説します。

Pythonによるオブジェクト指向プログラミング

Pythonはオブジェクト指向プログラミングの一つです。オブジェクト指向では、値やそれらを処理するメソッドを一つにまとめてオブジェクトとして扱います。

本記事では、Pythonでオブジェクト指向プログラミングをしていくために必要な知識と具体的なクラス定義・使い方について説明していきます。

クラスとインスタンス

オブジェクト指向でプログラミングを行っていくには「クラス」と「インスタンス」について理解しておく必要があります。クラスとインスタンスについて文章で書いてみると以下のようになります。

名称概要
クラスプログラムで表現する対象に関してデータ(変数)と処理(メソッド)を一つにまとめた設計書
インスタンスクラス定義(設計書)から実際に実体化した個別のモノ(オブジェクト)

文章だと何となく分かりづらいと思うので、例を使ってイメージを説明したいと思います。以下は、Person(人間)というクラスを作ることを想定して書いてみたイメージ図です。

Python クラスとインスタンス

上記例でクラスは、人間はどういった属性(変数)の値を持っているか、どういった機能(メソッド)があるかをまとめた設計書になっています。

そして、人間の特徴を持つ具体的な人「田中さん」や「佐藤さん」がインスタンスです。変数の部分に具体的にfirst_name=太郎、last_name=田中と入り、実体化しているイメージです。

また、少し違う表現をするとすれば、クラスは「分類」「種類」で、インスタンスは「具体的な実例」とも言えます。

人間の他で例えるとすると「犬」「猫」「国」「先生」…等を表現するものもクラスです。そして、犬であれば「ポチ、タロー、…」、国であれば「日本、アメリカ、中国、…」というのがインスタンスになります。

また、人間は挨拶をしたり、自分の名前を言って自己紹介したりといった動作をすることができます。このような機能はクラスのメソッドで表現し、インスタンスとなる田中さんや佐藤さんは挨拶したり(「こんにちは」)、名前を言ったり(「私の名前は、田中太郎です。」)することができます。

メソッドを呼び出すことは、メッセージを送って仕事を頼む様子に似ていることからメッセージパッシングとも呼ばれます。

以降でPythonでの具体的な定義方法や使い方を紹介していきます。この記事ではオブジェクト指向については細かくは説明しません。オブジェクト指向でのなぜプログラミングするのかといった点については「オブジェクト指向でなぜ作るのか 第3版」という書籍が参考になりおすすめです。

Note

上記で記載した説明のような様々な比喩表現がオブジェクト指向を分かりにくくしていると言う方もいます。なぜなら、現実世界では設計書から人間を作るようなことはなく、say_hello()とメッセージを送ったら「こんにちは」と言ってくれるとは限らないためです。

また、インスタンスが紐づくクラスは一つだけですが、現実世界ではそうとは限りません。田中さんはある側面では医者かもしれませんし、ある面では父親かもしれません。

比喩表現のこのような現実との乖離がオブジェクト指向を分かりにくくしているとも言われています。そのため、オブジェクト指向が「現実世界をそのままプログラミングするんだ」というような言い方は少し言い過ぎなのかなと思います。

ただし、オブジェクト指向は、ソフトウェアの再利用性、保守性を高める技術であることは間違いありません。あくまでプログラミングの技術として、クラス、インスタンス、その他にも継承、ポリモーフィズム(多相性)といった考え方があると割り切って勉強するのがよいかと思います。

私がクラスを「設計書」とよく言うのは、現実を表現するというよりも、あくまでプログラム設計であるという思いを含んでいます。

Pythonにおけるクラスとメソッドの定義

Pythonでクラスを定義する場合は、以下の例のような構成が基本となります。以降では以下の例を使ってクラスの定義方法について解説していきます。

class Person:
    """人間クラス"""

    kind = "human"

    def __init__(
        self, first_name=None, last_name=None, sex=None, birthday=None
    ):
        self.first_name = first_name
        self.last_name = last_name
        self.sex = sex
        self.birthday = birthday

    def say_myname(self):
        print(f"私の名前は、{self.last_name} {self.first_name} です。")

    @staticmethod
    def say_hello(greeting):
        print(greeting)

    @classmethod
    def get_kind(cls):
        return cls.kind

    def __del__(self):
        print(f"{self.last_name} {self.first_name} : delete")


def main():
    # クラスからインスタンスを生成
    tanaka = Person("太郎", "田中", "male", "2000/1/1")
    # クラスのメソッドを呼び出し
    tanaka.say_hello("こんにちは")
    tanaka.say_myname()
    print("---")

    # 別のインスタンスを生成
    sato = Person("愛子", "佐藤", "female", "2001/9/1")
    sato.say_hello("こんばんは")
    sato.say_myname()
    print("---")

    # クラスメソッドの呼び出し
    print(Person.get_kind())
    print("---")


if __name__ == "__main__":
    main()
【実行結果】
こんにちは
私の名前は、田中 太郎 です。
---
こんばんは
私の名前は、佐藤 愛子 です。
---
human
---
田中 太郎 : delete
佐藤 愛子 : delete

クラス定義

Pythonでは、以下のようにクラスを定義します。

class Person:

クラス名の命名規則についてはCapWords方式という方式を使います。CapWords方式は、各単語の頭文字を大文字で表す表記方法です。例えば複数単語を並べる場合には、FormView、DbClient等のように命名します。

Pythonの文法上はアンダースコア(_)も使えますが、利用しない方がよいでしょう。また、クラス定義は以下のように記載することも可能です。

class Person():
class Person(object):

「Person:」「Person():」という記載方法はPython3から使えるようになった記法でいずれも明示的に記載はしていないですが、objectクラスを継承したものとなっています。

「Person():」という記載方法の()は冗長な括弧としてチェックされて、例えばフォーマットツールのblackでは()は削除されるので、こちらの書き方はあまりよくないのかなと思っています。

「Person(object):」という記載は明示的にベースとなるクラスを指定できるので、こちらの方がよいという人もいます。この辺りは各プロジェクトごとに統一するのがよいでしょう。

Note

クラス名等の命名規則はPEP8に従うケースが多いため、参考にしてみてください。

クラスのメソッド

クラス内で定義される関数の事をメソッドと言います。クラスのメソッドには、いくつかの種類があります。以降でそれぞれの定義方法等について説明していきます。

コンストラクタ

クラスからインスタンスを生成する際にまず最初に呼び出される初期化メソッドをコンストラクタと言います。

コンストラクタは__init__で定義します。クラスのメソッドは引数としてインスタンス自身を表すselfを指定し、それに引き続いて初期化のために必要な入力引数があれば指定します。

    def __init__(
        self, first_name=None, last_name=None, sex=None, birthday=None
    ):
        self.first_name = first_name
        self.last_name = last_name
        self.sex = sex
        self.birthday = birthday

「self.変数名」で指定されるものを「インスタンス変数」と言います。

この変数はインスタンスに紐づく変数で、コンストラクタはインスタンス生成時にインスタンス変数に値をセットしたり、その他の初期化に必要な処理を記載したりします。

デストラクタ

インスタンス化したオブジェクトが破棄されるタイミングで呼び出されるメソッドをデストラクタと言います。

デストラクタは、__del__で定義します。__init__同様にselfを引数に取ります。

    def __del__(self):
        print(f"{self.last_name} {self.first_name} : delete")

デストラクタはインスタンスが参照されなくなって不要になった際に呼び出されます。今回の例では、削除されるインスタンスの氏名とdeleteという文字を表示してみています。

よくあるのはデータベースへのセッションを削除する等の使用していた資源を開放する処理等を書くことが多いかと思います。

Note

__init__や__del__のように、アンダースコア(_)2つで囲まれた関数は特殊メソッドと言われます。

メソッド

クラスのメソッドは、普通の関数と同様にdefでクラス内に定義します。引数としてはインスタンス自身を表すselfを指定し、必要があればそれに続いてメソッドで使用する引数を指定します。

    def say_myname(self):
        print(f"私の名前は、{self.last_name} {self.first_name} です。")

メソッドは、インスタンス変数にアクセスできるので、この例では、苗字、名前を使って氏名をprintしています。同様にその他の各種メソッドを定義することが可能です。

クラスメソッド

インスタンスを生成しなくても使用できるメソッドをクラスメソッドと言います。クラスメソッドは「クラス名.メソッド名(引数)」で使用することができます。

class Person:
    """人間クラス"""

    kind = "human"

...(途中省略)...

    @classmethod
    def get_kind(cls):
        return cls.kind

クラスメソッドは、メソッドの上に「@classmethod」デコレータを記載します。

通常のメソッドでは引数として受け取るのはselfと書きますが、クラスメソッドの場合は、一般的にclsを指定します。他に引数があれば、clsに続いて記載します。

クラスメソッドについてはクラスで共通の変数(上記例ではkindのようなもの)等にアクセスする場合に使用することがあります。

この例のメソッドを外部から具体的に呼び出す場合には「Person.get_kind()」といった方法で呼び出すことができ、インスタンスを作る必要はありません。

スタティックメソッド

引数としてselfやclsを受け取らず、クラス内のインスタンス変数を使用しないメソッドをスタティックメソッドと言います。

@staticmethod
def say_hello(greeting):
    print(greeting)

スタティックメソッドは、メソッドの上に「@staticmethod」デコレータを記載します。

スタティックメソッドは、クラスのインスタンス変数を使用しないため、クラス外で関数として定義しても問題ありません。

ただし、クラス内に記載した方が何の処理か分かりやすい場合等はインスタンス変数を使用しないとしてもクラス内に定義をする場合がありますので、スタティックメソッドの事は覚えておくとよいでしょう。

クラスのインスタンス化と使用方法

クラスをインスタンス化して使う場合には「変数名 = クラス名(引数, …)」で実体化します。クラスをインスタンス化して使用しているのは、main関数の以下の部分になります。

def main():
    # クラスからインスタンスを生成
    tanaka = Person("太郎", "田中", "male", "2000/1/1")
    # クラスのメソッドを呼び出し
    tanaka.say_hello("こんにちは")
    tanaka.say_myname()
    print("---")

    # 別のインスタンスを生成
    sato = Person("愛子", "佐藤", "female", "2001/9/1")
    sato.say_hello("こんばんは")
    sato.say_myname()
    print("---")

    # クラスメソッドの呼び出し
    print(Person.get_kind())
    print("---")
【実行結果】(再掲)
こんにちは
私の名前は、田中 太郎 です。
---
こんばんは
私の名前は、佐藤 愛子 です。
---
human
---
田中 太郎 : delete
佐藤 愛子 : delete

クラスからインスタンスを生成する場合には、「tanaka = Person(“太郎”, “田中”, “male”, “2000/1/1”)」「sato = Person(“愛子”, “佐藤”, “female”, “2001/9/1”)」のように記載します。

この時、メソッド定義で出てきたselfの部分は特に指定する必要はありません。自動的に自身のインスタンスが渡されます。引数で渡した値によってコンストラクタによる初期化が行われます。

メソッドを呼び出す場合は「tanaka.say_hello(“こんにちは”)」「tanaka.say_myname()」のように呼び出すことができます。また、クラスメソッドについては、特にインスタンス化することなく「Person.get_kind()」で実行できていることが分かります。

最後にデストラクタですがmain()の処理を抜ける際に呼び出されて「田中 太郎 : delete」「佐藤 愛子 : delete」というようにデストラクタ内の処理が呼び出されていることが分かります。

インスタンス変数の隠ぺい

オブジェクト指向を学んだ方であれば、カプセル化についてどのように対応するのか気になるかと思いますので、ここではインスタンス変数の隠ぺいについて簡単に触れます。

カプセル化とは「利用者側に関係ないインスタンス変数、メソッドなどを隠ぺいすること」です。

利用者側に不必要なアクセスをさせると不正なデータ変更などがされてしまい想定しないバグにつながる可能性があるため、カプセル化することはプログラムの品質を向上させることに役立ちます。

Pythonのクラスにおけるインスタンス変数は、publicであり外部からアクセスすることが可能です。「インスタンス名.変数名」のようにアクセスして変更することができます。

クラスのインスタンス変数を隠ぺいするためには、インスタンス変数の名前を「__」(アンダースコア×2個)で始まるように命名します。

また、Pythonではインスタンス変数に「_」(アンダースコア×1個)で始まるように命名して、インスタンス変数がクラス内部用(private)であることを示す記載方法もよく用いられます。

しかし、「_」(アンダースコア×1個)で定義した変数は、実際に隠ぺいされるわけではなく、普通にアクセス可能です。Pythonプログラマの間での紳士協定のようなものになっているということは理解しておきましょう。

以下の例でもう少し具体的に見ていきましょう。

class Person:
    """人間クラス"""

    kind = "human"

    def __init__(
        self, first_name=None, last_name=None, sex=None, birthday=None
    ):
        self.__first_name = first_name
        self.__last_name = last_name
        self._sex = sex
        self._birthday = birthday

    def say_myname(self):
        print(f"私の名前は、{self.__last_name} {self.__first_name} です。")


def main():
    tanaka = Person("太郎", "田中", "male", "2000/1/1")
    tanaka.say_myname()
    print(tanaka._sex)
    print(tanaka._birthday)
    # 隠ぺいされた変数へのアクセス
    # マングリングした名前
    # print(tanaka._Person__first_name)
    print(tanaka.__first_name)


if __name__ == "__main__":
    main()
【実行結果例】
私の名前は、田中 太郎 です。
male
2000/1/1
Traceback (most recent call last):
...(省略)...
AttributeError: 'Person' object has no attribute '__first_name'

上記例のように「__」(アンダースコア×2個)から始まるインスタンス変数にアクセスしようとすると、AttributeErrorということで例外が発生します。

ただし、クラス内のsay_mynameメソッドからは普通に__first_nameや__last_nameにアクセスできていることからカプセル化できていることが分かるかと思います。

一方、「_」(アンダースコア×1個)の変数はクラス内部用の意図で宣言していますが、tanaka._sex、tanaka._birthdayのようにクラス外部から普通にアクセスできていることが分かります。

なお、上記はインスタンス変数のみについて説明しましたが、メソッドでも同様です。クラス内部でのみ利用するメソッドについては積極的に「__」(アンダースコア×2個)から始まるメソッド名にするようにしましょう。

「__」(アンダースコア×2個)を付けても実は完全に隠ぺいされているわけではありません。もとの変数は「_[クラス名][元の名前]」にリネームされているためアクセスができなくなっているだけです。そのため、上の例だとtanaka._Person__first_nameとするとアクセス可能です。このように名前を変える機能をマングリングと言います。

上記の例でコメントアウトされている以下の部分を有効にして実行してみてください。

def main():
    tanaka = Person("太郎", "田中", "male", "2000/1/1")
    tanaka.say_myname()
    print(tanaka._sex)
    print(tanaka._birthday)
    # 隠ぺいされた変数へのアクセス
    # マングリングした名前
    print(tanaka._Person__first_name)
    # print(tanaka.__first_name)
【実行結果】
私の名前は、田中 太郎 です。
male
2000/1/1
太郎

上記のようにマングリングされた後の変数名でアクセスするとアクセスできてしまいます。あえてこのようなことをする必要はないわけですが、このような動作になっているということを理解しておくとよいでしょう。

まとめ

Pythonでクラスを定義する方法及び使い方について基本的な部分を解説しました。

クラスの定義方法や各種メソッドの定義方法について説明し、オブジェクト指向プログラミングで重要なカプセル化をPythonではどのように扱うのかについて説明しました。

オブジェクト指向プログラミングのためには今回説明したようなクラスや各種メソッドの定義についてしっかりと把握しておくことが大事になりますので、しっかりと理解してもらえればなと思います。