概要(メソッドは「そのオブジェクトができること」を表す操作)
メソッドは、クラスに属する関数で「オブジェクトの振る舞い」を定義します。インスタンスの状態(属性)を読み書きしたり、外部と連携したり、目的に応じて処理をまとめられます。まず押さえるべきは、インスタンスメソッド・クラスメソッド・スタティックメソッドの違い、selfの意味、そして「引数→検証→処理→返り値」の設計の型です。
メソッドの基本(self・引数・返り値の型を整える)
selfの意味(インスタンス自身を指す参照)
インスタンスメソッドの第1引数selfは「このオブジェクト自身」です。self.nameのように属性へアクセスし、状態を使って処理します。呼び出し側はselfを渡しません(Pythonが自動で渡します)。
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)
print(p.greet())
Python引数と返り値の型ヒント(契約を明確に)
型ヒントを付けると、補完が効いて誤りを減らせます。デフォルト値で使いやすくし、バリデーションは入口で行います。
class Calculator:
def total(self, price: float, tax_rate: float = 0.1) -> float:
if price < 0 or not (0 <= tax_rate <= 1):
raise ValueError("priceは0以上、tax_rateは0〜1")
return price * (1 + tax_rate)
Python3種類のメソッド(インスタンス・クラス・スタティック)
インスタンスメソッド(最も一般的な振る舞い)
インスタンスの状態を使う処理に向きます。selfにアクセスして属性を読む・書くのが前提です。
class Counter:
def __init__(self):
self.n = 0
def inc(self) -> None:
self.n += 1
def value(self) -> int:
return self.n
Pythonクラスメソッド(クラスに紐づく工場や設定操作)
@classmethodは第1引数にcls(クラス自身)を受けます。代替コンストラクタや、クラス全体の設定変更に向いています。
class User:
def __init__(self, user_id: str, name: str):
self.user_id = user_id
self.name = name
@classmethod
def from_dict(cls, data: dict) -> "User":
return cls(data["id"], data.get("name", "unknown"))
Pythonスタティックメソッド(文脈上ここに置きたい純粋関数)
@staticmethodはself/clsを受けません。クラスに関連するが状態に依存しない“補助関数”をまとめる場所として使います。
class StringUtil:
@staticmethod
def slugify(s: str) -> str:
return s.lower().replace(" ", "-")
PythonWeb/APIで使う形(クライアント・サービス・モデル)
APIクライアントのメソッド設計(設定を握り、操作を提供)
セッションやヘッダーをinitで準備し、HTTP操作をメソッドに分割します。タイムアウトやエラー処理を各メソッドで一貫させます。
import requests
class ApiClient:
def __init__(self, base_url: str, api_key: str, timeout: float = 5.0):
if not base_url.startswith("https://"):
raise ValueError("HTTPSのみ許可")
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サービス層のメソッド(ドメイン整形を担当)
外部I/Oとロジックを分離し、返却値を「使いやすい形」に整えます。ここで境界値や欠損の扱いを統一します。
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モデルのメソッド(表現・変換・検証)
データモデルはto_dictやvalidateのような“自分に関する操作”をメソッドとして持つと扱いやすくなります。
class UserModel:
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"
def to_dict(self) -> dict:
return {"id": self.user_id, "name": self.name}
Python使いやすいインターフェース(プロパティ・特殊メソッド・チェーン)
@propertyで自然なアクセス(読み取り・検証付き書き込み)
計算値や不変条件を守りたい属性はプロパティ化します。setterで検証すれば、常に整合した状態を保てます。
class Rectangle:
def __init__(self, w: float, h: float):
self._w = w; self._h = h
@property
def area(self) -> float:
return self._w * self._h
Python特殊メソッドで“ふるまい”を統合
strで表示、eqで比較、reprでデバッグ、callで「関数のように呼べる」など、Pythonらしい使い心地を提供できます。
class Price:
def __init__(self, amount: float, currency: str = "JPY"):
self.amount = amount; self.currency = currency
def __str__(self) -> str:
return f"{self.amount:.2f} {self.currency}"
def __eq__(self, other: object) -> bool:
return isinstance(other, Price) and (self.amount, self.currency) == (other.amount, other.currency)
Pythonメソッドチェーン(戻り値でつなぐ)
副作用を抑えるなら、変更後の新インスタンスを返して連鎖させます。読みやすい“手続きの線”が作れます。
class Query:
def __init__(self, base: str, params: dict | None = None):
self.base = base; self.params = params or {}
def add(self, k: str, v: str) -> "Query":
new = dict(self.params); new[k] = v
return Query(self.base, new)
def build(self) -> str:
qs = "&".join(f"{k}={v}" for k, v in self.params.items())
return f"{self.base}?{qs}"
url = Query("https://example.com").add("q", "python").add("page", "1").build()
Pythonよくあるつまずきと回避(self・責務・可変データの扱い)
selfの書き忘れと引数不一致
メソッド定義の第1引数にselfを忘れるとTypeErrorになります。エディタのシグネチャ警告を活かし、雛形から書く癖をつけましょう。
なんでもメソッドに詰め込みすぎる
「検証→処理→整形」を1メソッドでやり過ぎるとテストが難しくなります。I/O(外部)とロジック(内部)は別メソッド・別クラスに分け、単体テスト可能な粒度に保ちます。
可変データを共有して壊す
クラス属性にlist/dictを置くと全インスタンスで共有されます。インスタンス固有の状態はself側に持ち、変更の影響範囲を限定しましょう。
例題(小さなAPIラッパーを設計して動かす)
設定→クライアント→サービスの3層でメソッドを配置
from dataclasses import dataclass
import requests
@dataclass
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}")
return {"id": data.get("id", ""), "name": data.get("name") or "unknown"}
Pythonこの構成だと、I/OはClientのメソッド、ドメイン整形はServiceのメソッドと役割が明確で、テストもモックしやすくなります。
まとめ(「誰が何をするか」をメソッドに刻む)
メソッドは、オブジェクトの振る舞いそのもの。selfで状態に触れるインスタンスメソッドを基本に、クラス全体の文脈は@classmethod、純粋な補助は@staticmethodへ分けます。引数と返り値の契約を型ヒントで明示し、検証は入口で、I/Oとロジックは分離してテストしやすく。プロパティや特殊メソッドで使い心地を整え、チェーンや不変設計で副作用を抑える。この型を体に入れれば、初心者でもメソッドを「読みやすく、壊れにくく、拡張しやすく」設計できます。
