クラス

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

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

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

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

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

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

クラスとインスタンス

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

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

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

Python クラスとインスタンス

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

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

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

注意点:実世界との違い

オブジェクト指向は、現実世界の比喩で説明されることが多いですが、これは混乱を招くこともあります。

現実では、設計図から人を作ることはありませんし、メッセージを送ったら必ず意図通りに反応するとは限りません。また、インスタンスは通常 1 つのクラスに属しますが、現実では 1 人が複数の役割を持つこともあり得ます(例:医者であり、父親である)。

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

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

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)。

また、Python 3 では「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プログラマ間での約束事であり、強制力はありませんが、多くの Python プログラマは理解していることです。また、IDEによっては、アンダースコア 1 個の変数は、隠ぺいされているとして補完には表示されないように工夫されています。

一方で、__」(アンダースコア 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 においてカプセル化がどのように扱われているのかも説明しました。

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

ソースコード

上記で紹介しているソースコードについては GitHub にて公開しています。参考にしていただければと思います。

あわせて読みたい
【Python Tech】プログラミングガイド
【Python Tech】プログラミングガイド
ABOUT ME
ホッシー
ホッシー
システムエンジニア
はじめまして。当サイトをご覧いただきありがとうございます。 私は製造業のメーカーで、DX推進や業務システムの設計・開発・導入を担当しているシステムエンジニアです。これまでに転職も経験しており、以前は大手電機メーカーでシステム開発に携わっていました。

プログラミング言語はこれまでC、C++、JAVA等を扱ってきましたが、最近では特に機械学習等の分析でも注目されているPythonについてとても興味をもって取り組んでいます。これまでの経験をもとに、Pythonに興味を持つ方のお役に立てるような情報を発信していきたいと思います。どうぞよろしくお願いいたします。

※キャラクターデザイン:ゼイルン様
記事URLをコピーしました