概要(インスタンス変数は「各オブジェクト固有の状態」)
インスタンス変数は、クラスから作られた各インスタンスが個別に持つデータです。同じクラスでも、インスタンスごとに値が違ってよい(むしろ違うのが普通)というのがポイントです。定義は通常、init内でself.xxxに代入します。クラス変数(全体共有)と混同しないこと、可変データを安全に初期化すること、更新の入口を決めることが重要です。
基本の作り方(initと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}歳)です!"
p1 = Person("太郎", 20)
p2 = Person("花子", 25)
print(p1.greet()) # 太郎の情報
print(p2.greet()) # 花子の情報(p1とは別の状態)
Pythonこの例のnameとageが、各インスタンスに固有のインスタンス変数です。p1の変更はp2に影響しません。
インスタンス変数の更新と参照
p1.age += 1
print(p1.age) # 21
print(p2.age) # 25(影響なし)
Python「その人の年齢」というコンテキストに沿って、個別に更新されます。
重要ポイントの深掘り(クラス変数との違い・可変の安全化・契約)
クラス変数との違い(共有か個別か)
クラス変数は全インスタンスで共有される設定・定数に向きます。インスタンス変数は個別の状態に向きます。可変データ(list/dict)は必ずインスタンス変数にして、混線を防ぎます。
class Counter:
unit = "回" # クラス変数(共有)
def __init__(self):
self.n = 0 # インスタンス変数(個別)
Python可変データの初期化(デフォルト引数の落とし穴回避)
# 悪い例:全インスタンスで同じリストが共有される
class BagBad:
def __init__(self, items: list = []):
self.items = items
# 良い例:Noneを受けて新規生成する
class Bag:
def __init__(self, items: list | None = None):
self.items = [] if items is None else list(items)
Pythonインスタンスごとに別々のリスト/辞書を持たせるのが安全です。
契約(前提)の明示で壊れない初期化にする
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入口で前提をチェックしてからインスタンス変数へ格納すると、後段のロジックが安定します。
アクセスの設計(公開面・内部面・プロパティ)
内部用はアンダースコア、公開は@propertyで守る
class Rectangle:
def __init__(self, w: float, h: float):
self._w = w
self._h = h
@property
def width(self) -> float:
return self._w
@width.setter
def width(self, v: float):
if v <= 0:
raise ValueError("widthは正の数")
self._w = v
@property
def area(self) -> float:
return self._w * self._h
Python直接書き換えると壊れやすい属性は内部用(_name)に置き、公開面は@property経由で検証付きにします。
更新の入口を用意して整合性を維持
class Profile:
def __init__(self, name: str):
self._name = name
def rename(self, new_name: str):
if not new_name.strip():
raise ValueError("名前は空不可")
self._name = new_name
Python一箇所にルールを集約しておくと、状態の整合性が保てます。
仕組み理解(属性探索・dict・slots)
属性探索(インスタンス→クラス→親クラス)
アクセスはまずインスタンスのdict(個別の状態)を探し、なければクラスや親へ進みます。インスタンスに同名があればそれが優先されます。
class Config:
timeout = 5.0 # クラス側のデフォルト
c = Config()
print(c.timeout) # 5.0(クラスの値)
c.timeout = 10.0 # インスタンス側に上書き(インスタンス変数ができる)
print(c.timeout) # 10.0
Pythondictで中身を覗く
class A:
def __init__(self):
self.x = 1
a = A()
print(a.__dict__) # {'x': 1}
Pythonどんなインスタンス変数があるか確認できます。
slotsで属性を固定して軽量化
大量生成する小さなオブジェクトは、許可する属性名を固定してメモリ削減・タイプミス防止が可能です。
class Point:
__slots__ = ("x", "y")
def __init__(self, x: float, y: float):
self.x = x
self.y = y
PythonWeb / APIでの実践(モデル・セッション・設定の扱い)
モデルで揺れるレスポンスを吸収して属性化
class UserModel:
def __init__(self, data: dict):
self.user_id = str(data.get("id", ""))
self.name = data.get("name") or "unknown"
def to_dict(self) -> dict:
return {"id": self.user_id, "name": self.name}
Pythonインスタンス変数へ最小保証の形で格納すると、後続処理がシンプルになります。
クライアントで再利用する接続を属性に持つ
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セッションやタイムアウトはインスタンス変数に持ち、各メソッドから再利用します。
設定の不変化と安全な公開
class Settings:
def __init__(self, base_url: str, api_key: str):
self._base_url = base_url
self._api_key = api_key
@property
def base_url(self) -> str:
return self._base_url
@property
def api_key(self) -> str:
return self._api_key
Python外から安易に書き換えられないように、不変寄せで公開します。
例題(インスタンス変数設計で小さなAPIラッパーを整える)
設定→クライアント→サービスの流れ
from dataclasses import dataclass
import requests
@dataclass(frozen=True)
class Settings:
base_url: str
api_key: str
class Client:
def __init__(self, s: Settings, timeout: float = 5.0):
self.base_url = s.base_url
self.timeout = timeout
self.session = requests.Session()
self.session.headers.update({"Authorization": f"Bearer {s.api_key}"})
def get(self, path: str) -> dict:
r = self.session.get(f"{self.base_url}{path}", timeout=self.timeout)
r.raise_for_status()
return r.json()
class UserService:
def __init__(self, client: Client):
self.client = client
def user(self, uid: str) -> dict:
data = self.client.get(f"/users/{uid}")
u = UserModel(data)
return u.to_dict()
Python各クラスがインスタンス変数で必要な状態を持ち、責務が分離されるため、テストと拡張が楽になります。
まとめ(「個別の状態はインスタンス変数」で一貫性を保つ)
インスタンス変数は各オブジェクト固有の状態を表し、initでself.xxxに格納して初期化します。クラス変数(共有)と混同せず、可変データは必ずインスタンス変数へ。公開面は@propertyで守り、更新の入口を用意して整合性を維持する。属性探索やdict/slotsの仕組みを理解して、動作を予測可能にする。これを型として徹底すれば、初心者でも「壊れない・読みやすい」状態管理が自然にできるようになります。
