Python | OOP:init

Python Python
スポンサーリンク

概要(initは「生まれた直後の初期設定」を行う特別なメソッド)

initは、インスタンス生成時に自動で呼ばれる“コンストラクタ”で、属性の初期化や前提チェックを行います。Pythonではメソッドの第1引数にself(そのインスタンス自身)が渡されるため、init内でself.xxxに値を格納して状態を作ります。


基本の書き方(selfで属性を作り、初期値や前提を入れる)

最小例と動作の流れ

class Person:
    def __init__(self, name: str, age: int):
        self.name = name      # ここで“その人”の属性を作る
        self.age = age

    def greet(self) -> str:
        return f"こんにちは、{self.name}{self.age}歳)です!"

p = Person("太郎", 23)  # ここで __init__ が自動で呼ばれる
print(p.greet())
Python
  • インスタンス作成時にinitが自動実行され、self.nameやself.ageのようなインスタンス属性が設定されます。

selfの意味(必須の第1引数)

selfは「今まさに初期化しているインスタンス」への参照です。呼び出し側はselfを渡しませんが、定義側は必ず第1引数にselfを書きます。


初期化の設計(バリデーション・デフォルト・型ヒント)

前提チェックを入口で弾く

class User:
    def __init__(self, user_id: str, name: str | None = None):
        if not user_id:
            raise ValueError("user_idは必須です")
        self.user_id = user_id
        self.name = name or "unknown"
Python
  • 入口で前提が破れていないかを確認し、後段が安心して使える状態にしてから属性を設定します。

デフォルト値とオプション引数

class ApiClient:
    def __init__(self, base_url: str, timeout: float = 5.0):
        if not base_url.startswith("https://"):
            raise ValueError("HTTPSのみ許可")
        self.base_url = base_url
        self.timeout = timeout
Python
  • よく使う値はデフォルトにして、必須の値だけ明示的に受け取り、初期化の負担を減らします。

型ヒントで“契約”を明示

引数と属性の型を示すと、エディタ補完や静的チェックが効いて、初期化の誤りが減ります。


継承時の初期化(super().initで親の準備を呼ぶ)

親の初期化を忘れない

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

class Dog(Animal):
    def __init__(self, name: str, breed: str):
        super().__init__(name)  # 親の初期化を呼ぶ
        self.breed = breed
Python
  • 親が用意する属性や前提があるなら、子クラスのinitでsuper().initを呼んで“親の準備”を済ませます。

上書きと追記の考え方

子クラスでは「親の初期化+子の追加属性」が基本です。親の契約(前提チェックや必須属性)を保ちつつ、必要な拡張だけ足します。


よくあるつまずきと回避(self・戻り値・重い処理)

selfの書き忘れ

  • 症状: TypeError(引数数が合わない)。
  • 対策: メソッド定義の第1引数は必ずself。Pythonが呼び出し時に自動で渡します。

initは値を“返さない”

  • 原則: initの戻り値はNoneでなければならず、returnでオブジェクトを返しません。インスタンスはクラス呼び出し時に作られ、initはその初期化だけを担います。

初期化で重いI/Oをしない

  • 症状: 生成だけでネットワークやファイルI/Oが走り、テストと再利用が難しくなる。
  • 対策: initは軽く保ち、接続や読込はconnect/loadなど別メソッドに切り出します。

実例で身につける(Web/APIの現実的パターン)

セッションを準備するクライアント

import requests

class ApiClient:
    def __init__(self, base_url: str, api_key: str, timeout: float = 5.0):
        self.base_url = base_url
        self.timeout = timeout
        self.session = requests.Session()
        self.session.headers.update({"Authorization": f"Bearer {api_key}"})

    def get_user(self, uid: str) -> dict:
        r = self.session.get(f"{self.base_url}/users/{uid}", timeout=self.timeout)
        r.raise_for_status()
        return r.json()
Python
  • initで「再利用するセッションとヘッダー」を作り、各メソッドから使い回すと効率的で安全です。

設定注入→サービス層での整形

class Settings:
    def __init__(self, base_url: str, api_key: str):
        self.base_url = base_url
        self.api_key = api_key

class UserService:
    def __init__(self, client: ApiClient):
        self.client = client

    def profile(self, uid: str) -> dict:
        data = self.client.get_user(uid)
        return {"id": data.get("id", ""), "name": data.get("name") or "unknown"}
Python
  • 依存(クライアントや設定)をinitで受け取り、メソッドでドメイン整形に集中します。

補助トピック(dataclass・デフォルトの落とし穴)

dataclassで初期化を自動生成

from dataclasses import dataclass

@dataclass
class UserDTO:
    id: str
    name: str | None = None
Python
  • init/repr/比較などを自動生成してくれるため、「属性を持つだけの容器」なら学習初期に便利です。

ミュータブルなデフォルトは避ける

# 悪い例
class Bag:
    def __init__(self, items: list = []):  # すべてのインスタンスで共有され続ける
        self.items = items

# 良い例
class Bag:
    def __init__(self, items: list | None = None):
        self.items = [] if items is None else items
Python
  • listやdictをデフォルト引数にすると、全インスタンスで共有されるため予期せぬ蓄積が起きます。Noneを受けて中で新規生成するのが安全です。

まとめ(initは「軽く、正しく、再利用の起点」を作る)

initはインスタンス誕生直後の初期化地点。selfで属性を作り、前提を入口で確認し、デフォルト値で使いやすくする。継承ではsuper().initで親の準備を呼び、重い処理は別メソッドへ切り出す。dataclassで単純な容器は自動化し、ミュータブルなデフォルトは避ける。これを型として徹底すれば、初心者でも「壊れにくく、テストしやすく、拡張しやすい」初期化設計が自然に身につきます。

タイトルとURLをコピーしました