コンテンツにスキップ

クラス

クラス

以下にクラスの定義の仕方の例を示す。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
class ClassName:
    # 正確にはクラス属性というらしい
    class_member = "クラス変数"

    def __init__(self) -> None:
        self.__private_member = "プライベート変数"
        print("コンストラクタ")

    def member_method(self) -> None:
        print("メンバーメソッド")

    def __private_member_method(self) -> None:
        print("プライベートメンバーメソッド")

    # getter
    @property
    def member(self) -> str:
        print("ゲッター")
        return self.__private_member

    # setter
    @member.setter
    def member(self, value: str) -> None:
        print("セッター")
        self.__private_member = value

    # クラスメソッド
    @classmethod
    def class_method(cls) -> None:
        cls.class_member = "書き換わったクラス変数"
        print("クラスメソッドはクラス変数を書き換えられる")

    # 静的メソッド
    @staticmethod
    def static_method() -> None:
        print("静的メソッドはクラス変数を書き換えられない")


def main():
    c = ClassName()
    print(ClassName.class_member)
    print(c.member)
    c.member = "書き換わったプライベート変数"
    print(c.member)
    c.member_method()
    ClassName.class_method()
    print(ClassName.class_member)
    ClassName.static_method()


if __name__ == "__main__":
    main()

抽象クラス(インターフェース)

abc.ABCクラスを継承させることで抽象クラスを定義することができる。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
from abc import ABC, abstractmethod

class ReaderGateway(ABC):
  @abstractmethod
  def read(self) -> str:
    pass

class ReaderService(ReaderGateway):
  def read(self) -> str:
    return input("> ")

def main():
    reader_gateway = ReaderService()
    reader_gateway.read()


if __name__ == "__main__":
    main()

データクラス

@dataclassを使うとデータクラスを定義できる。

データクラスはクラスと異なり__init__()__repr__()__eq__()などのメソッドが自動生成される。それ以外はクラスと同じのようで、データクラスにメソッドやクラスメソッドをもたせることもできる。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from dataclasses import dataclass


@dataclass
class Server:
    ip_address: str
    port: int


# データを格納したいだけならデータクラスを使えばコンストラクタを定義しなくて済む分、楽である
server = Server("127.0.0.1", 80)
print(f"{server.ip_address}:{server.port}")

イミュータブルなデータクラスと完全コンストラクタ

@dataclass(frozen=True)を使うとイミュータブルなデータクラスを定義できる。

以下の点でいまいちである。

  • ミュータブルなメンバー(例: listdictclass)にはイミュータブルは適用されない
  • イミュータブルなメンバーを変更するようなコードを書いても VSCode は構文エラーを出してくれず、実行時エラーになる
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
from dataclasses import dataclass
from typing import Self


@dataclass(frozen=True)
class Todo:
    id: str
    status: str

    def __post_init__(self: Self) -> None:
        # __post_init__でメンバーの値の妥当性をチェックできる
        if self.status not in ["notStartedYet", "inProgress", "done"]:
            raise Exception("Invalid status")


immutable = Todo("aaa", "notStartedYet")
immutable.status = "bbb"
print(immutable)