Python講座 / クラスとインスタンスの基本

クラスは関連するデータと処理をまとめる定義で、インスタンスはその定義から作る個々のオブジェクトです。同じクラスのインスタンスでも、それぞれ独立した名前や金額を保持できます。

定義と個々のオブジェクトを区別する

Expenseは支出を表す型の定義で、昼食とバス代はその個別のインスタンスです。属性に状態を保存し、メソッドでその状態に関係する処理を表します。単純な計算をすべてクラスへ変える必要はありません。

インスタンスメソッドの最初の引数selfには、呼び出した対象が渡されます。selfは慣例的な名前です。__init__は作成されたインスタンスを初期化します。通常のfirst.describe()という呼び出しでselfを手動で渡す必要はありません。

属性とアクセス規則は、インスタンス属性とクラス属性で扱う規則とつながります。

二つの支出を作って実行する

classes_demo.pyへ次の全体を保存し、仮想環境でpython classes_demo.pyを実行します。金額の単位は例を通して韓国ウォン(KRW)です。

class Expense:
    def __init__(self, name, amount):
        if amount < 0:
            raise ValueError("negative amount")
        self.name = name
        self.amount = amount

    def describe(self):
        return f"{self.name}: {self.amount:,} KRW"

first = Expense("lunch", 8000)
second = Expense("bus", 2500)
print(first.describe())
print(second.describe())
print(first.amount + second.amount)

予想結果は次のとおりです。表示処理は共通でも、参照するnameとamountは呼び出したインスタンスのものです。

lunch: 8,000 KRW
bus: 2,500 KRW
10500

初期化と責務の範囲を決める

この初期化は金額が負でないことを調べますが、入力が数値であることを前提としています。文字列、bool、小数も扱うなら、受け入れる型と単位の規則を別に定めてください。初期化時に正しくても、後からfirst.amount = -1を代入すると、この検査は働きません。

支出の計算、ファイル保存、ネットワーク通信、画面表示を一つのクラスへ詰め込むと変更理由が増えます。支出そのものの規則と、保存方法などの外部とのやり取りを分けると、個別に確認しやすくなります。

独立性と更新時の規則を確認する

first.amountだけを9000へ変更し、second.amountが2500のままであることを確かめます。負の値で新しいExpenseを作るとValueErrorになることも確認し、最後に元の値へ戻します。

Expense.describe(first)とfirst.describe()の結果を比較すると、束縛された呼び出しの意味を確認できます。属性名の誤字で別の属性を作ることがあるため、命名とテストが重要です。型ヒント、dataclass、テストは役割が異なります。更新時にも規則を守らせる方法はプロパティで扱います。

selfはそのメソッドを呼び出されたインスタンスです。同じクラスから作った二つのオブジェクトも、別々のインスタンス属性を持てます。名前→オブジェクト→その属性という関係で読むと、呼び出し時にselfがどの対象を指すか追えます。

名前 別のオブジェクト インスタンス属性
first object A first.amount = 3500
second object B second.amount = 8000

表のfirstとsecondは関係を示すための名前です。一方のインスタンス属性を変えても他方の属性は変わりません。

関連する公式リファレンス