結論から言うと、__init__ では「このオブジェクトが最初から持っているべき情報」を明確に決めることが大切です。

Pythonでクラスを書き始めると、__init__ に何を書けばよいのか迷いやすいです。
とりあえず値を入れて動かすことはできますが、必須の値とあとで変わる値が混ざると、使いにくいクラスになりやすくなります。
この記事では、__init__ で初期状態を設計する基本を、動くコードと一緒に整理します。

この記事でわかること

  • __init__ がどんな役割を持つメソッドなのか
  • 必須の値と初期値をどう分けるとわかりやすいか
  • 実務で扱いやすい初期状態を決める考え方

完成コード

完成コードは次の通りです。

class Invoice:
    def __init__(self, client_name, amount, due_date, is_paid=False):
        self.client_name = client_name
        self.amount = amount
        self.due_date = due_date
        self.is_paid = is_paid

    def mark_paid(self):
        self.is_paid = True

    def build_summary(self):
        status = "入金済み" if self.is_paid else "未入金"
        return f"{self.client_name} | {self.amount}円 | 支払期限: {self.due_date} | {status}"


invoice = Invoice("株式会社サンプル", 120000, "2026-04-30")
print(invoice.build_summary())

invoice.mark_paid()
print(invoice.build_summary())

コードのポイント

このコードでは、請求情報を表す Invoice クラスの初期状態を __init__ でそろえています。

  • client_name amount due_date は、作成時点で必須の情報です
  • is_paid=False は、最初は未入金とみなす初期値です
  • mark_paid() を呼ぶと、作成後に状態を変更できます

最初に必要な情報だけを __init__ に集めると、そのクラスが何を表すのかが読みやすくなります。

コードを順番に説明します

主要な処理を分けて説明します。

1. 作成時点で必須の値を引数に入れます

class Invoice:
    def __init__(self, client_name, amount, due_date, is_paid=False):
        self.client_name = client_name
        self.amount = amount
        self.due_date = due_date
        self.is_paid = is_paid

__init__ は、インスタンスを作るときに最初に呼ばれるメソッドです。
ここでは、請求先、金額、支払期限を必須の引数にしています。

このように、オブジェクトを作る時点で必ず必要な値は、__init__ の引数で受け取るとわかりやすいです。
あとから設定する前提の値まで全部必須にすると、呼び出し側が使いづらくなります。

2. 毎回同じ値になりやすいものは初期値を用意します

    def __init__(self, client_name, amount, due_date, is_paid=False):
        self.client_name = client_name
        self.amount = amount
        self.due_date = due_date
        self.is_paid = is_paid

請求書を作った直後は、通常まだ入金されていません。
そのため is_paid=False を初期値として入れています。

このように、最初の状態がほぼ決まっている値は、デフォルト引数で初期値を与えると呼び出しが簡単になります。
毎回同じ値を渡さなくて済むため、使い方のぶれも減らせます。

3. 作成後に変わる状態は別メソッドで更新します

    def mark_paid(self):
        self.is_paid = True

    def build_summary(self):
        status = "入金済み" if self.is_paid else "未入金"
        return f"{self.client_name} | {self.amount}円 | 支払期限: {self.due_date} | {status}"

__init__ の役割は、最初の状態をそろえることです。
作成後に変わる処理まで __init__ に詰め込むのではなく、状態変更は別メソッドに分けたほうが整理しやすくなります。

この例では、入金が確認できたときに mark_paid() を呼びます。
初期化と更新を分けることで、クラスの役割が見えやすくなります。

実務で使うときのポイント

実務では、__init__ を書くときに「このオブジェクトが成立する最低条件は何か」を先に決めると設計しやすいです。

  • 作成に必須な情報だけを引数にすると、呼び出し側で迷いにくくなります
  • ほぼ固定で始まる状態は初期値を使うと、コードの重複を減らせます
  • 計算や外部アクセスまで __init__ に入れすぎると、初期化の責務が重くなります
  • 後から更新する値は、専用メソッドや別処理に分けると保守しやすいです

たとえば業務スクリプトでも、設定情報、処理対象、実行結果のように役割が違う値を整理しておくと、あとで機能追加しやすくなります。
__init__ は単に値を代入する場所ではなく、そのクラスの入口を決める場所だと考えると使いやすいです。

よくある勘違い・注意点

  • __init__ に何でも入れればよいわけではなく、重い処理を入れすぎると扱いにくくなります
  • 必須ではない値まで大量に引数へ並べると、呼び出し側が読みにくくなります
  • まだ決まっていない値を無理に __init__ へ入れるより、あとで設定する設計のほうが自然なこともあります
  • 初期化と状態更新の役割を分けると、クラスの責務が整理しやすいです

まとめ

  • __init__ では、オブジェクトが最初から持つべき情報をそろえます
  • 必須の値と初期値を分けて考えると、使いやすいクラスになります
  • 作成後に変わる処理は別メソッドに分けると、設計が読みやすくなります
  • 実務では、そのクラスが成立する最低条件を先に決めることが大切です