クラス

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

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

Pythonでクラスの定義方法と使い方を解説します。

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

Pythonは、オブジェクト指向、関数型、手続き型など複数のプログラミングパラダイムをサポートするマルチパラダイム言語です。その中でもオブジェクト指向プログラミングでは、データと処理を一つにまとめてオブジェクトとして扱います。

この記事では、Pythonでオブジェクト指向プログラミングを行うための基礎知識と、クラス定義やその利用方法について説明します。

クラスとインスタンス

オブジェクト指向プログラミングの基本概念として「クラス」と「インスタンス」があります。

名称概要
クラスデータ(変数)と処理(メソッド)を一つにまとめた設計書のことです。
インスタンスクラスをもとに生成された具体的なオブジェクトです。

例えば、以下のような「Person(人間)」というクラスを考えてみましょう。

Python クラスとインスタンス

Personクラスでは、人間の属性(変数)や処理(メソッド)を定義した設計書です。そして、「田中さん」や「佐藤さん」といった具体的な人物がインスタンスに当たります。クラスが「種類・分類」を表し、インスタンスが「具体的な実例」として存在します。

他の例として「犬」「猫」「国」などがクラスに相当し、「ポチ」「タマ」「アメリカ」などが具体的なインスタンスです。

クラスのメソッドには、インスタンスに対して行える操作を定義します。例えば、挨拶(say_hello)や自己紹介(say_myname)といったものです。メソッドの呼び出しには、インスタンスにメッセージを送って行動を依頼するイメージであり、これをメッセージパッシングと呼びます。

注意点:実世界との違い

オブジェクト指向は、現実世界の比喩で説明されることが多いですが、これは混乱を招くこともあります。現実では、設計図から人を作ることはできませんし、メッセージを送ったら必ず意図通りに反応するとは限りません。また、インスタンスは通常一つのクラスに属しますが、現実では一人が複数の役割を持つこともあり得ます(例:医者であり、父親である)。

オブジェクト指向はあくまでプログラミング技術の一つであり、現実世界をそのまま再現するものではありません。重要なのは、オブジェクト指向がソフトウェアの再利用性や保守性を高めるための手法であるという点です。クラスやインスタンス、継承、ポリモーフィズム(多態性)といった概念をプログラム設計の視点で学ぶことが重要です。

この記事では、オブジェクト指向の詳細についてはこれ以上踏み込みませんが、興味があれば「オブジェクト指向でなぜ作るのか」という書籍が参考になるでしょう。

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

Pythonでクラスを定義する際の基本構成は以下の通りです。例として、Personクラスを用いて説明します。

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

クラス定義

クラス名は「CapitalizedWords (CapWords または CamelCase)」という方式を使います(例: PersonFormViewDbClient)。

また、Python3では「class Person:」「class Person():」「class Person(object):」のように記載できますが、いずれもobjectを継承しています。記法については各プロジェクトごとに統一しましょう。

クラスの命名はPEP8に従うのが一般的です。PEP8の命名規則は「コーディング規約 PEP8の命名規則」にまとめているので参考にしてください。

メソッド定義

クラス内の関数は「メソッド」と呼ばれます。メソッドには以下のような種類があります。

コンストラクタ

コンストラクタは、インスタンス生成時に初期化を行うメソッドでdef __init__(self, 入力引数, ...):というように定義します。インスタンス変数は、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は、インスタンス自身を指し、クラス内で定義されたメソッドにおいて、自身のインスタンス変数や他のメソッドにアクセスするために使用します。Pythonでは、メソッドの最初の引数としてselfを指定することで、メソッドがどのインスタンスに対して呼ばれているかを明確にします。

デストラクタ

デストラクタは、インスタンス破棄の際に実行されるメソッドでdef __del__(self):というように定義します。

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

デストラクタは、例えばファイルやDBのリソース解放処理などを記載する際によく使用されます。

なお、__init____del__のように、アンダースコア2個(__)で囲まれたメソッドは「特殊メソッド」と言われます。

通常のメソッド

通常のメソッドでは、関数と同様にdef メソッド名(self):というように定義します。

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

クラスメソッド

メソッドの上に「@classmethod」デコレータを記載したメソッドをクラスメソッドと言います。クラスメソッドは、インスタンスを生成しなくても「クラス名.メソッド名(引数)」として使用可能です。

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

    kind = "human"

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

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

通常のメソッドなどでは引数としてselfを受け取りますが、クラスメソッドの場合は、引数としてclsを指定します。クラスメソッドは、クラスで共通変数(上記例では、kindのようなもの)にアクセスする場合に使用することがあります。

スタティックメソッド

メソッドの上に「@staticmethod」デコレータを記載したメソッドをスタティックメソッドと言い、インスタンス変数にアクセスしないメソッドです。インスタンスの操作をしないため、クラスの外部で関数として定義することも可能ですが、必要に応じてクラス内にスタティックメソッドとして定義します。

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

インスタンス化とメソッドの使用

ここからはインスタンス化とメソッドの使用方法を見てみましょう。

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")のように生成します。メソッドの呼び出しは、tanaka.say_hello("こんにちは")のように、クラスメソッドは、Person.get_kind()のように呼び出しが可能です。

インスタンス変数の隠ぺいとカプセル化

カプセル化とは、外部からの不要なアクセスを防ぐためにインスタンス変数やメソッドを隠ぺいするオブジェクト指向における重要な考え方です。

Pythonでは、インスタンス変数を_」(アンダースコア1個)で始めることで非公開であることを意図します。ただし、Pythonではこのアンダースコア1個の変数自体アクセスを制限する効力はなく、直接アクセスできてしまいます。アンダースコア1個の変数にアクセスしないというのは、Pythonプログラマ間での約束事であり、強制力はないものになっています。

一方で、__」(アンダースコア2個)で始まるインスタンス変数は、外部からのアクセスはできなくなります。ただし、この方法も実は完全に隠ぺいされているわけではなくマングリングというリネーム処理が行われています。

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

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.__first_name)


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

上記例では、tanaka._sextanaka._birthdayというアンダースコア1個の変数にはアクセスできていることが分かります。一方で、tanaka.__first_nameというアンダースコア2個の変数にはアクセスできずにAttributeErrorとなります。

では次に、以下のコードに変更して実行してみてください。

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

上記例では、tanaka._Person__first_nameとすることで、__first_name変数の値にアクセスができています。マングリングは、もともとの変数を「_[クラス名][元の名前]」という形式にリネームしているだけであるため、完全な隠ぺいができているというわけではありません。

外部からアクセスされたくない変数に「__」(アンダースコア2個)をつけることは有効ですが、完全な隠ぺいではないということは理解しておきましょう。

まとめ

Pythonでクラスを定義方法と使い方について解説しました。

オブジェクト指向におけるクラスやメソッドの概念を説明し、Pythonにおける具体的な実装例を紹介しました。また、Pythonにおいてカプセル化がどのように扱われているのかも説明しました。

オブジェクト指向プログラミングは、非常に重要なプログラミング技術の一つです。ぜひ、考え方も含めてしっかりと理解してもらいたいと思います。