Skip to content
This page has been auto-translated and may contain errors.View in English

クラスとオブジェクト

これまで扱ってきたすべての型(文字列、リスト、辞書)は、実はクラスです。"hello".upper() を呼び出すとき、あなたは文字列オブジェクトのメソッドを呼び出しています。クラスを使うと、独自のデータと振る舞いを持った独自の型を定義できます。Player クラスは名前、スコア、レベルを保持し、自分自身を表示する方法を知ることができます。

クラスはユーザー定義型を実現するための仕組みです。クラスはテンプレートを定義します: 各インスタンスが保持するデータ(属性)とそれがサポートする操作(メソッド)です。並列の変数で値を追跡してそれらをあちこちに渡す代わりに、明確なインターフェースを持つ一つのまとまったオブジェクトにまとめます。

class 文はメタクラス(デフォルトは type)を使って現在の名前空間に新しい型オブジェクトを作成します。インスタンスはクラスを呼び出すことで生成され、__new__ がオブジェクトを割り当て、__init__ がそれを初期化します。Pythonのオブジェクトモデルは統一されています: クラス、関数、型そのものを含め、すべてがオブジェクトです。

設計図とインスタンス

クラスは設計図です。インスタンスはその設計図から作られた具体的な存在です。必要な数だけインスタンスを作ることができ、それぞれが独自のデータを持ちながら、クラスで定義された同じメソッドを共有します。

クラスは構造と振る舞いを定義します。インスタンスはそのクラスから生成されたオブジェクトです。各インスタンスは独自の属性名前空間を持ちますが、クラスのメソッドオブジェクトを共有します。インスタンスの生成は関数のようにクラスを呼び出すことで行われます: Dog() は新しい Dog インスタンスを生成します。

クラスを呼び出すと type.__call__ が呼ばれ、cls.__new__(cls) でインスタンスを割り当て、cls.__init__(instance, ...) で初期化します。生成されたオブジェクトの __class__ はそのクラスを指します。メソッド検索はMROに従います: インスタンスの __dict__、クラスの __dict__、そしてMROチェーンの順です。

python
class Dog:
    def bark(self):
        print("Woof!")

rex  = Dog()
luna = Dog()

rex.bark()    # "Woof!"
luna.bark()   # "Woof!"

Dog はクラスです。rexluna はインスタンスです: 2匹の異なる犬で、それぞれがクラスで定義された同じ振る舞いを共有しています。

__init__self

__init__ は、新しいインスタンスを作成するときにPythonが自動的に呼び出すメソッドです。ここでオブジェクトの初期データをセットアップします。self は、メソッドが操作対象の特定のインスタンスを参照する方法で、常に最初のパラメータです。

__init__ は新しく割り当てられたインスタンスを初期化します。self はすべてのインスタンスメソッドの最初のパラメータの慣例的な名前です。alice.display() を呼び出すと、Pythonは自動的にインスタンスを渡します。__init__ 内で self に設定された属性はインスタンス属性です: 各インスタンスが独自のコピーを持ちます。

__init____new__ から既に割り当てられたインスタンスを受け取り、それを設定します。self はキーワードではありません。これは最初の位置パラメータで、instance.method() を呼び出すときにデスクリプタプロトコルによって暗黙的に渡されます。self.attr = value を設定すると object.__setattr__ が呼ばれ、これはインスタンスの __dict__ に書き込みます。__init__ で設定されない属性も、Pythonの辞書は動的なので、後からどのインスタンスにも設定できます。

python
class Player:
    def __init__(self, name, score=0):
        self.name  = name
        self.score = score

    def add_points(self, points):
        self.score += points

    def display(self):
        print(f"{self.name}: {self.score} points")

あきら = Player("あきら")
ゆうき   = Player("ゆうき", score=50)

あきら.add_points(30)
あきら.display()   # "あきら: 30 points"
ゆうき.display()     # "ゆうき: 50 points"

self.nameself.scoreインスタンス属性です: それらはクラス自体ではなく、特定のオブジェクトに属します。各 Player インスタンスは独自の namescore を持ちます。

メソッド

クラス内で定義された関数はすべてメソッドです。インスタンスメソッドは常に self を最初のパラメータとして持ちます。Pythonがそれを自動的に渡します。メソッドは self を通してインスタンスのデータを読み書きできます。

インスタンスメソッドはクラスの名前空間に格納される通常の関数です。デスクリプタプロトコルが instance.methodバウンドメソッドに変換し、self を自動的にバインドします。メソッドから self を返すことで、メソッドチェーンが可能になります: obj.scale(2).rotate(90)

instance.method はデスクリプタ検索を引き起こします: type.__getattribute__ がクラスの __dict__ 内で method を見つけ、それを関数(デスクリプタプロトコルを実装している)として認識し、インスタンスが事前に適用された関数をラップするバウンドメソッドオブジェクトを返します。これが self が自動的に渡される仕組みです。self を返すことでフルエントインターフェースが可能になります。

python
class Circle:
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return 3.14159 * self.radius ** 2

    def scale(self, factor):
        self.radius *= factor
        return self    # selfを返すことでチェーン可能: c.scale(2).scale(0.5)

c = Circle(5)
print(c.area())    # 78.53975
c.scale(2)
print(c.area())    # 314.159

クラス変数とインスタンス変数

クラスに直接定義された変数(__init__ の内部ではなく)はクラス変数です。すべてのインスタンスが同じクラス変数を共有します。__init__ 内で self に設定された変数はインスタンス変数で、各オブジェクト固有のものです。

クラス変数はクラスの __dict__ に存在し、すべてのインスタンスで共有されます。インスタンス変数は各インスタンスの __dict__ に存在します。self.attr を読み取るとき、Pythonはまずインスタンスを、次にクラスをチェックします。self.attr = value を書き込むとき、それは常にインスタンス属性を作成または更新し、クラス変数が存在する場合はそれをシャドウします。

インスタンスでの属性検索は次の順序に従います: インスタンスの __dict__、クラスの __dict__、そしてMRO。self.attr への書き込みは常にインスタンスの __dict__ を対象とします(__setattr__ がオーバーライドされていない限り)。クラス変数は、self.attr への書き込みがインスタンスのシャドウを作成するまで共有されます。可変なクラス変数(リストなど)は特に厄介です: すべてのインスタンスが同じオブジェクトを共有するため、任意のインスタンスを介した変更はすべてのインスタンスから見えます。

python
class Player:
    max_lives = 3    # クラス変数、すべてのPlayerで共通

    def __init__(self, name):
        self.name  = name   # インスタンス変数、各Player固有
        self.lives = Player.max_lives

    def die(self):
        self.lives -= 1

あきら = Player("あきら")
ゆうき   = Player("ゆうき")

Player.max_lives = 5    # 現在と将来のすべてのインスタンスで変更

すべてのインスタンスで共有される値にはクラス変数を使います: 定数、カウンター、デフォルト値など。オブジェクトごとに異なるデータにはインスタンス変数を使います。

__str____repr__

__str__print() やf文字列がオブジェクトをどう表示するかを制御します。__repr__ はコンソールやデバッグで表示される開発者向けの表示を制御します。常に __repr__ を定義しましょう。デバッグ表示とは別にユーザー向けのきれいな表示が欲しいときは __str__ を定義しましょう。

__str__str()print()、およびf文字列によって呼び出されます。__repr__repr() によって呼び出され、REPLに表示されます。__repr__ のみが定義されている場合、Pythonはそれを両方に使用します。慣例として: __repr__ はオブジェクトを再構築できる文字列を返すべきです。__str__ は人間が読みやすい要約を返すべきです。

str(obj)type(obj).__str__(obj) を呼び出し、__str__ が定義されていない場合は __repr__ にフォールバックします。repr(obj)type(obj).__repr__(obj) を呼び出します。f文字列内では、{obj}__format__ を呼び出し、デフォルトでは __str__ を呼び出します。{obj!r} はフォーマットの前に repr() を適用します。__repr__ の慣例: 評価したときにオブジェクトを再現する ClassName(arg1=..., arg2=...) 形式の文字列を返します。

python
class Player:
    def __init__(self, name, score):
        self.name  = name
        self.score = score

    def __str__(self):
        return f"{self.name} ({self.score} pts)"

    def __repr__(self):
        return f"Player(name={self.name!r}, score={self.score})"

あきら = Player("あきら", 87)
print(あきら)        # "あきら (87 pts)"   (__str__を使用)
repr(あきら)         # "Player(name='あきら', score=87)"  (__repr__を使用)

常に __repr__ を定義しましょう。デバッグ表示とは別にユーザー向けのきれいな表現が欲しいときは __str__ を定義しましょう。__repr__ のみが定義されている場合、Pythonはそれを両方に使用します。

プライベートの慣例

Pythonには真の意味でのプライベート変数はありませんが、名前の先頭にアンダースコアを1つ付けること(_balance)は「これは内部用なので、クラスの外から直接使わないでください」というシグナルとなる慣例です。言語によって強制されるものではなく、他の開発者へのコミュニケーションです。

シングルアンダースコア(_attr)は内部使用を示す慣例です。Pythonはこれを強制しませんが、すべてのリンター、IDE、開発者はこれを尊重します。ダブルアンダースコア(__attr)は名前マングリングを引き起こします: Pythonはそれを _ClassName__attr に書き換え、サブクラスでの偶発的な衝突を防ぎます。これは真のプライバシーではなく、衝突回避メカニズムです。

シングルアンダースコア: 慣例のみ、強制なし。ダブルアンダースコア: 名前マングリングが実行時ではなくコンパイル時に __attr_ClassName__attr に変換します。これはサブクラス衝突ガードであり、プライバシーメカニズムではありません。マングリングされた名前は依然として外部からアクセス可能です。__slots__ はより根本的な制御です: インスタンスの __dict__ を固定されたスロットデスクリプタのセットで置き換え、任意の属性作成を防ぎ、メモリオーバーヘッドを削減します。

python
class BankAccount:
    def __init__(self, balance):
        self._balance = balance    # _は「触らないで」を意味する

    def deposit(self, amount):
        if amount > 0:
            self._balance += amount

    def balance(self):
        return self._balance

ダブルアンダースコア(__name)は名前マングリングを引き起こします。Pythonはサブクラスでの衝突を避けるために属性を _ClassName__name にリネームします。これが必要になることはめったにありません。シングルアンダースコアがほとんどのコードでの慣例です。

継承

クラスは別のクラスから継承でき、そのすべての属性とメソッドを自動的に取得します。その後、サブクラスで特定のメソッドをオーバーライドして、その振る舞いを変更できます。これにより、共通の基底クラスを再利用しつつ、必要な箇所で特殊化できます。

継承は「is-a」関係を作ります。サブクラスは親のすべてのメソッドと属性を継承し、そのいずれもオーバーライドできます。メソッド解決順序(MRO)が属性の検索順序を定義します。Pythonは多重継承をサポートしています。MROはC3線形化アルゴリズムで計算されます。

MROはC3線形化を使用して type.__mro_entries__ で計算され、ClassName.__mro__ としてアクセス可能です。メソッド解決はMROを左から右にたどります。isinstance(obj, cls) はMROをたどってチェックします。多重継承は強力ですが、ダイヤモンド継承を生むことがあります。MROはこれを決定論的に解決します。super() は単に直接の親クラスではなく、MROに従います。

python
class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        return "..."

class Dog(Animal):
    def speak(self):
        return f"{self.name} says Woof!"

class Cat(Animal):
    def speak(self):
        return f"{self.name} says Meow!"

pets = [Dog("Rex"), Cat("Luna"), Dog("Max")]
for pet in pets:
    print(pet.speak())

DogCatAnimal から __init__ を継承するので、自身のものを持つ必要はありません。speak() を自分たち固有の振る舞いでオーバーライドします。

super()

super() は親クラスのメソッドを呼び出します。親の振る舞いを完全に置き換えるのではなく、拡張したい場合に使用します: 親の __init__ を呼び出してそのセットアップを実行し、その上にサブクラスに必要なものを追加します。

super() はMRO内の次のクラスにメソッド呼び出しを委譲するプロキシオブジェクトを返します。親が __init__ を持つ場合、サブクラスの __init__ から常に super().__init__() を呼び出しましょう。これをスキップすると、親のセットアップコードが実行されず、オブジェクトが壊れた状態のままになる可能性があります。

引数なしの super() (Python 3) は、__class__ (コンパイラによって注入されるセル変数)と囲んでいるメソッドの最初の引数を使ってMROの位置を決定します。super(cls, self) は明示的な形式です。プロキシは type(self).__mro__ 内の cls の次のクラスから始まるメソッド検索を解決します。これは正しい多重継承に不可欠です: super() は完全なMROを通して各クラスの __init__ を連鎖させます。

python
class Animal:
    def __init__(self, name, sound):
        self.name  = name
        self.sound = sound

class Dog(Animal):
    def __init__(self, name):
        super().__init__(name, "Woof")   # Animal.__init__を呼び出す
        self.tricks = []                  # 追加のものを足す

    def learn(self, trick):
        self.tricks.append(trick)

rex = Dog("Rex")
rex.learn("sit")
print(rex.tricks)   # ["sit"]

サブクラスが独自の __init__ を持ち、親も持っている場合は常に super().__init__() を呼び出しましょう。

クラスメソッドと静的メソッド

@classmethod はインスタンスではなくクラス自身を受け取るメソッドを作成します。代替のコンストラクタに便利です: 文字列、ファイル、または別の形式からインスタンスを作成します。@staticmethod は組織化の目的でクラス内に存在するただの関数で、インスタンスもクラスも受け取りません。

@classmethod は最初の引数としてインスタンスではなく cls (クラス)を受け取ります。主な用途は、異なる入力形式からインスタンスを作成する代替コンストラクタです。@staticmethod はクラス下の名前空間に置かれた通常の関数です。クラスやインスタンスへのアクセスはありません。コンストラクタには @classmethod を、論理的にクラスに関連するユーティリティ関数には @staticmethod を使用しましょう。

@classmethod はデスクリプタプロトコルを使用します: classmethod デスクリプタは、最初の引数がインスタンスではなくクラスになるバウンドメソッドを返します。cls は呼び出し時の実際のクラスなので、サブクラスのメソッドはサブクラスを取得します。@staticmethod は、暗黙的な最初の引数を持たずに、基礎となる関数を変更せずに返す、より単純なデスクリプタです。両デコレータはデスクリプタの動作を変更します。どちらも関数のコードオブジェクトには影響しません。

python
class Player:
    def __init__(self, name, score):
        self.name  = name
        self.score = score

    @classmethod
    def from_string(cls, data):
        name, score = data.split(",")
        return cls(name, int(score))

あきら = Player.from_string("あきら,87")
python
class Player:
    @staticmethod
    def is_valid_name(name):
        return name.isalpha() and len(name) >= 2

Player.is_valid_name("あきら")   # True
Player.is_valid_name("A1")      # False

代替コンストラクタには @classmethod を使用しましょう。論理的にクラスに属するが、インスタンスやクラスのデータを必要としないユーティリティ関数には @staticmethod を使用しましょう。

@property

@property を使うと、括弧なしで、メソッドを属性のようにアクセスできます。他の属性から計算される値で、シンプルな属性アクセスとして読むのが自然な場合に使います。

@property はメソッドを読み取り専用の属性に変えます。属性にアクセスするとメソッドが実行されます。これは保存されたデータから派生する計算値や、公開インターフェースを変更せずに属性アクセスに検証を追加するのに便利です。対応する @name.setter を付けると属性が書き込み可能になります。

@property はデスクリプタです: property(fget, fset, fdel, doc)instance.attr にアクセスすると fget(instance) が呼び出されます。@name.setterfset を設定します。@name.deleterfdel を設定します。プロパティはインスタンスではなくクラスで検索されるため、instance.__dict__[name] はプロパティをシャドウしません(データデスクリプタはインスタンスの __dict__ よりも優先されます)。プロパティはJava風のゲッター/セッターメソッドのPython的な代替手段です。

python
class Circle:
    def __init__(self, radius):
        self.radius = radius

    @property
    def area(self):
        return 3.14159 * self.radius ** 2

    @property
    def diameter(self):
        return self.radius * 2

c = Circle(5)
print(c.area)      # 78.53975 (属性のように見えて、メソッドのように実行される)
print(c.diameter)  # 10

プロパティは計算値に便利です: 他の属性から派生し、() なしでアクセスするのが自然なものに。

実践

インスタンス属性、メソッド、@property__str__ を持つ Player クラス:

python
class Player:
    max_lives = 3

    def __init__(self, name: str):
        self.name  = name
        self.score = 0
        self.lives = Player.max_lives

    def earn_points(self, amount: int) -> None:
        self.score += amount

    def take_hit(self) -> bool:
        self.lives -= 1
        return self.lives > 0

    @property
    def is_alive(self) -> bool:
        return self.lives > 0

    def __str__(self) -> str:
        return f"{self.name} | Score: {self.score} | Lives: {self.lives}"

あきら = Player("あきら")
あきら.earn_points(50)
あきら.take_hit()
print(あきら)            # "あきら | Score: 50 | Lives: 2"
print(あきら.is_alive)   # True

@property ゲッターを持つプライベート属性、deactivate メソッド、to_dict シリアライザを使用する User クラス:

python
class User:
    def __init__(self, user_id: int, username: str, email: str):
        self.id       = user_id
        self.username = username
        self.email    = email
        self._active  = True

    @property
    def active(self) -> bool:
        return self._active

    def deactivate(self) -> None:
        self._active = False

    def to_dict(self) -> dict:
        return {
            "id":       self.id,
            "username": self.username,
            "email":    self.email,
            "active":   self._active,
        }

    def __repr__(self) -> str:
        return f"User(id={self.id}, username={self.username!r})"

あきら = User(1, "あきら", "[email protected]")
print(あきら.to_dict())
あきら.deactivate()
print(あきら.active)   # False

プロパティの背後にtrain/validationのスライス処理をカプセル化し、きれいなデバッグ出力のための __repr__ を持つ DataSplit クラス:

python
class DataSplit:
    def __init__(self, data: list, train_ratio: float = 0.8):
        split       = int(len(data) * train_ratio)
        self._train = data[:split]
        self._val   = data[split:]

    @property
    def train(self) -> list:
        return self._train

    @property
    def val(self) -> list:
        return self._val

    @property
    def sizes(self) -> tuple[int, int]:
        return len(self._train), len(self._val)

    def __repr__(self) -> str:
        return f"DataSplit(train={len(self._train)}, val={len(self._val)})"

data  = list(range(100))
split = DataSplit(data, train_ratio=0.8)
print(split)         # DataSplit(train=80, val=20)
print(split.sizes)   # (80, 20)

_train_val のアンダースコアの接頭辞は、呼び出し側が生のリストを直接変更するのではなく、プロパティを通すべきであることを示しています。Pythonはこれを強制しませんが、明確な契約を設定します。