Python のクラス定義の基本 (class)

class キーワードを使って、Python のクラスを定義することができます。

コンストラクタとインスタンスメソッド

メソッドは通常の関数と同様に def キーワードで定義していくのですが、__init__ という名前のメソッドを定義すると、インスタンス生成時(クラス名())に呼び出されるコンストラクタとして扱われます。 コンストラクタの中では、自分自身のインスタンスを参照するための self を使って、属性の初期化などを行います。

Person クラスを定義してみる
class Person:
    """クラスの説明をここに書く"""

    def __init__(self, name: str):
        """コンストラクタの説明をここに書く"""
        self.name = name

    def hello(self):
        """メソッドの説明をここに書く"""
        print(f"Hello, {self.name}!")

コンストラクタやメソッドの第 1 引数には、必ず自分自身のインスタンスを参照するための self 引数を配置します。 実際には、名前は何でもよいのですが、公式のスタイルガイドで self という名前を使うことが推奨されています(参考: PEP 8)。

上記のように定義したクラスは、次のように使用することができます。

person = Person("maku")  # インスタンスを生成する
person.hello()           # メソッドを呼び出す
実行例
$ python main.py
Hello, maku!

クラス属性 (class attribute)

クラス属性 は、インスタンスを生成せずにアクセスできる属性です。 次のクラスは、num というクラス属性を持っています。 クラス属性には、クラス名.クラス属性名 という形でアクセスできます。

クラス属性を定義する
# クラス属性を持つクラス
class Holder:
    num: int = 0

# クラス属性を参照する
print(Holder.num)  # => 0
Holder.num = 100
print(Holder.num)  # => 100

クラス定数も同様に定義できます。 名前を大文字にするだけです。

クラス定数を定義する
# クラス定数を持つクラス
class Author:
    NAME: str = "Maku"
    AGE: int = 14

# クラス定数を参照する
print(Author.NAME)  # => Maku
print(Author.AGE)   # => 14

クラスメソッド (class method)

クラスメソッド は、インスタンスを生成せずに呼び出せるメソッドで、@classmethod デコレーターを使って定義します。 クラスメソッドの第 1 引数には、自身のクラスメソッドを示す cls を配置します(この名前も、PEP 8 で推奨されています)。 クラスメソッドの中からは、cls.クラス属性名 という形で、クラス属性にアクセスできます。

クラスメソッドを定義する
class Holder:
    num = 0

    @classmethod
    def print(cls):
        print(cls.num)

if __name__ == "__main__":
    Holder.num = 1
    Holder.print()  # => 1

クラスメソッドは、ファクトリーメソッドの実装に使われることがあります。 次の mysterios_person メソッドは、特殊な Person インスタンスを生成するためのファクトリーメソッドです。

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

    @classmethod
    def mysterious_person(cls) -> "Person":
        return cls(name="John Doe", age=20)

# 使用例: ファクトリーメソッドでインスタンス生成
person = Person.mysterious_person()

静的メソッド (static method)

静的メソッド は、インスタンスを生成せずに呼び出せるメソッドで、@staticmethod デコレーターを使って定義します。 静的メソッドはクラスメソッドと異なり、第 1 引数で cls を受け取りません。 単なる関数とほぼ同等ですが、クラス内で静的メソッドとして定義することにより、そのクラスに関連するユーティリティ関数であることが明確になります。 また、クラスをインポートするだけで静的メソッドにアクセスできるようになるなどの利点もあります。

class Formatter:
    # ...

    @staticmethod
    def make_indent(level: int) -> str:
        return " " * 4 * level

メソッドや属性を private にする

クラス内のメソッドや属性を private 扱いにするには、メソッド名の前にアンダースコア _ を 1 つ付けます。 ただし、これは単なる慣習で、実際には外からアクセスできてしまいます。

class Person:
    def __init__(self, name: str, age: int):
        self._name = name
        self._age = age

if __name__ == "__main__":
    p = Person("Maku", 14)
    print(p._name)  # 見えちゃう
    print(p._age)   # 見えちゃう

外からアクセスできないようにするには、メソッド名の前にアンダースコアを 2 つ付けます。

class Person:
    def __init__(self, name: str, age: int):
        self.__name = name
        self.__age = age

if __name__ == "__main__":
    p = Person("Maku", 14)
    print(p.__name)  # エラー!
    print(p.__age)   # エラー!
☝️ Name Mangling アンダースコアを 2 つ付けたときに外から属性にアクセスできなくなるのは、Python の Name Mangling(名前修飾) という仕組みが働いているからです。 属性名がアンダースコア 2 つで始まっていると、Python は内部的にその属性名を _クラス名__属性名 という名前に置き換えます。 この仕組みにより、外部からの不用意なアクセスを避けるとともに、サブクラスで定義された属性の名前との衝突を防ぐ効果があります。