概要(属性は「オブジェクトが持つデータ」。設計と使い分けが要)
属性はクラスやインスタンスが持つデータで、振る舞い(メソッド)と対になる要素です。まず理解すべきは、インスタンス属性とクラス属性の違い、可変なデフォルトの落とし穴、アクセス制御の慣習(_name, __name)、そして@propertyによる安全な読み書きです。これらを押さえると、Web/APIコードでも「壊れない・読みやすい」状態管理ができます。
インスタンス属性とクラス属性(共有か個別かの線引き)
使い分けの基本
インスタンス属性は「各オブジェクトごとに異なる値」。クラス属性は「全インスタンスで共有したい定数や設定」。可変データは原則インスタンス側に置き、クラス属性は不変な値に限定すると事故が減ります。
class Counter:
unit = "回" # クラス属性(共有する定数)
def __init__(self):
self.n = 0 # インスタンス属性(個別の状態)
c1, c2 = Counter(), Counter()
c1.n += 1
c2.n += 2
print(c1.n, c2.n, Counter.unit) # 1 2 回
Python可変データの共有事故を避ける
クラス属性にlistやdictを置くと、全インスタンスで「同じもの」を共有します。意図せず連動するため、可変はインスタンス属性へ。
class Bag:
items = [] # NG: 全インスタンスで共有され続ける
class SafeBag:
def __init__(self):
self.items = [] # OK: 個別に持つ
Python初期化で属性を作る(initと型ヒント・バリデーション)
initで正しい状態を用意する
属性はinitでself.xxxとして定義し、前提チェックを入口で行います。型ヒントを添えると契約が明確になり、補完や静的チェックが効きます。
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デフォルトに可変を使わない
Noneを受けて中で新規生成する型にすれば、安全です。
class Tags:
def __init__(self, tags: list[str] | None = None):
self.tags = [] if tags is None else list(tags)
Pythonアクセス制御の慣習(公開・内部・名前マングリング)
命名で意図を伝える
先頭にアンダースコアを付けると「内部用」の意図を示せます。外部から触れるな、ではなく「触ると壊れやすい」というサインです。
class Account:
def __init__(self, balance: int):
self._balance = balance # 内部属性(直接操作は推奨しない)
Python名前マングリング(__name)で衝突回避
クラス内で先頭に二重アンダースコア(__secret)を付けると、クラス名に基づいて変形され、意図しない上書きや衝突を避けられます。
class Secure:
def __init__(self):
self.__token = "secret" # 外部からは Secure._Secure__token としてしか触れない
Python公開面は@propertyで整える
直接書き換えられると壊れる属性は@propertyで守り、setterで検証します。
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属性の仕組み(探索順序・dict・slots)
属性探索の順序を理解する
アクセス時は「インスタンス→クラス→親クラス…」の順に探されます。インスタンスに同名があればそちらが優先され、無ければクラス属性が使われます。これを利用して「デフォルトはクラス属性、上書きはインスタンス属性」が可能です。
class Config:
timeout = 5.0 # デフォルト
cfg = Config()
print(cfg.timeout) # 5.0
cfg.timeout = 10.0 # インスタンスに上書き
print(cfg.timeout) # 10.0
Pythondictで中身を覗く
インスタンスの属性は通常dictに入ります。どんな属性があるかの確認に便利です。
class A:
def __init__(self):
self.x = 1
a = A()
print(a.__dict__) # {'x': 1}
Pythonslotsで属性を制限・軽量化
大量生成する小さなオブジェクトでは、許可する属性名をslotsで固定するとメモリ削減・タイプミス防止になります。
class Point:
__slots__ = ("x", "y")
def __init__(self, x: float, y: float):
self.x = x; self.y = y
PythonWeb / API文脈の例(設定・モデル・クライアントの属性設計)
設定は不変に寄せる
設定値はクラス全体で共有したいならクラス属性、またはインスタンス生成後は読み取り専用の@propertyで守ります。
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レスポンスの揺れを吸収し、内部で扱いやすい形にするのが役目です。
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}"})
Pythonよくあるつまずきと回避(共有事故・隠蔽・更新ルール)
クラス属性の共有ミス
全インスタンスで値が連動して驚くケースは、可変データをクラス属性に置いたため。可変は必ずインスタンス属性で個別管理に切り替えます。
隠蔽し過ぎて使いづらい
全部を_で固めると、利用側が拡張できず窮屈になります。基本は「で内部を示し、公開面は@property」で十分です。
更新の入り口を一箇所に
値の整合性が重要なら、setterやupdateメソッドを通して変更させます。直接書き換えを避けると、バリデーションの抜け漏れを防げます。
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例題(属性の設計を通して安全な小さなアプリを作る)
設定→クライアント→サービスの属性設計
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.timeout = timeout
self.base_url = s.base_url
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で正しい初期化を行い、可変のデフォルトを避け、内部用は_で示す。重要な値は@propertyで守って検証し、更新の入口を用意する。探索順序とdict/slotsを理解して、動作とコストを制御する。これを型として徹底すれば、初心者でも「安全で見通しの良い」属性設計が自然にできるようになります。
