Pythonを学んでいると「クラスって何?」「オブジェクト指向って難しそう…」と感じたことはありませんか?安心してください。クラスの概念は一度理解すると、コードが劇的に整理され、大規模なプログラムも書けるようになります。この記事では、クラスの基礎から継承、@propertyデコレータまで、実践的なコード例を通じてわかりやすく解説します。

🏗️ クラスとは何か?

クラスは「設計図」のようなものです。例えば、「車」というクラスを作れば、その設計図から何台でも車(インスタンス)を作り出すことができます。

プログラムでいうと、データ(属性)と機能(メソッド)をひとまとめにしたものがクラスです。

# クラスの定義 class Car: # コンストラクタ:インスタンス作成時に呼ばれる def __init__(self, make, model, year): self.make = make # インスタンス変数 self.model = model self.year = year self.speed = 0 # 初期値 def accelerate(self, amount): self.speed += amount return self def brake(self, amount): self.speed = max(0, self.speed - amount) return self def info(self): return f"{self.year}年式 {self.make} {self.model}(速度:{self.speed}km/h)" # インスタンス作成(クラスから実体を作る) my_car = Car("トヨタ", "プリウス", 2024) your_car = Car("ホンダ", "シビック", 2023) my_car.accelerate(60) print(my_car.info())
2024年式 トヨタ プリウス(速度:60km/h)
💡 selfとは? メソッドの第1引数 self は「このインスタンス自身」を指します。self.speed は「この車インスタンスの速度」という意味です。

🔑 __init__ コンストラクタの詳細

__init__(ダンダーinitと呼ぶ)は、インスタンスが作成されるときに自動的に呼ばれる特殊メソッドです。初期化処理をここに書きます。

class BankAccount: # クラス変数(全インスタンスで共有) interest_rate = 0.001 # 0.1% account_count = 0 def __init__(self, owner, initial_balance=0): # インスタンス変数(各インスタンスで独立) self.owner = owner self.balance = initial_balance self.transactions = [] BankAccount.account_count += 1 # クラス変数を更新 def deposit(self, amount): if amount <= 0: raise ValueError("入金額は正の値にしてください") self.balance += amount self.transactions.append(f"+{amount}") return self def withdraw(self, amount): if amount > self.balance: raise ValueError("残高不足です") self.balance -= amount self.transactions.append(f"-{amount}") return self def __str__(self): return f"[{self.owner}の口座] 残高: {self.balance:,}円" # 使用例 account = BankAccount("山田太郎", 10000) account.deposit(5000).deposit(3000).withdraw(2000) print(account) # __str__ が呼ばれる print(f"取引履歴: {account.transactions}")
[山田太郎の口座] 残高: 16,000円 取引履歴: ['+5000', '+3000', '-2000']

クラス変数 vs インスタンス変数

種類定義場所スコープ用途
クラス変数クラス直下全インスタンスで共有カウンター、設定値
インスタンス変数__init__内(self.xxx)各インスタンスで独立個別データ

📦 特殊メソッド(マジックメソッド)

__init__のように__で囲まれたメソッドを特殊メソッド(マジックメソッド)と呼びます。Pythonが自動的に呼び出す仕組みです。

class Vector: def __init__(self, x, y): self.x = x self.y = y def __str__(self): # print()で表示 return f"Vector({self.x}, {self.y})" def __repr__(self): # デバッグ用の公式表現 return f"Vector(x={self.x}, y={self.y})" def __add__(self, other): # + 演算子 return Vector(self.x + other.x, self.y + other.y) def __mul__(self, scalar): # * 演算子 return Vector(self.x * scalar, self.y * scalar) def __len__(self): # len()関数 import math return int(math.sqrt(self.x**2 + self.y**2)) def __eq__(self, other): # == 演算子 return self.x == other.x and self.y == other.y v1 = Vector(3, 4) v2 = Vector(1, 2) print(v1 + v2) # __add__ 呼び出し print(v1 * 2) # __mul__ 呼び出し print(len(v1)) # __len__ 呼び出し(3²+4²=25、√25=5)
Vector(4, 6) Vector(6, 8) 5
📝
__str__
print()時の表示形式。人間が読みやすい文字列を返す。
🔍
__repr__
デバッグ用の詳細表現。eval()で再現できる形式が理想。
__add__ / __sub__
+や-演算子をオーバーロード。独自の数値型に使う。
🔢
__len__ / __getitem__
len()やスライス操作を独自クラスで使えるようにする。

🧬 継承:クラスを拡張する

継承は、既存のクラス(親クラス)の機能を引き継ぎながら、新しいクラス(子クラス)を作る仕組みです。コードの再利用性が大幅に向上します。

# 親クラス(基底クラス) class Animal: def __init__(self, name, age): self.name = name self.age = age def breathe(self): return f"{self.name}は呼吸しています" def speak(self): return "..." # 子クラスでオーバーライド # 子クラス:Animalを継承 class Dog(Animal): def __init__(self, name, age, breed): super().__init__(name, age) # 親の__init__を呼ぶ self.breed = breed def speak(self): # メソッドオーバーライド return f"{self.name}({self.breed}): ワン!" def fetch(self): # Dogクラス固有のメソッド return f"{self.name}がボールを取ってきた!" class Cat(Animal): def speak(self): return f"{self.name}: ニャー…(冷たい目で)" # ポリモーフィズム:同じメソッドが各クラスで異なる動作 animals = [Dog("ポチ", 3, "柴犬"), Cat("タマ", 5)] for a in animals: print(a.speak()) print(a.breathe()) # 親のメソッドも使える
ポチ(柴犬): ワン! ポチは呼吸しています タマ: ニャー…(冷たい目で) タマは呼吸しています

super()の重要性super().__init__() を使うことで、親クラスの初期化処理を確実に実行できます。これを忘れると、親クラスのインスタンス変数が設定されず、バグの原因になります。

多重継承(Multiple Inheritance)

Pythonは複数の親クラスを持つ多重継承をサポートしています。

class Flyable: def fly(self): return f"{self.name}は空を飛んでいます" class Swimmable: def swim(self): return f"{self.name}は泳いでいます" class Duck(Animal, Flyable, Swimmable): def speak(self): return f"{self.name}: ガーガー!" donald = Duck("ドナルド", 7) print(donald.speak()) print(donald.fly()) print(donald.swim())
ドナルド: ガーガー! ドナルドは空を飛んでいます ドナルドは泳いでいます

🔒 カプセル化とアクセス制御

クラスの内部データを外から守る仕組みがカプセル化です。Pythonでは命名規則でアクセスレベルを示します。

class Temperature: def __init__(self, celsius): self.__celsius = celsius # プライベート変数(__prefix) # @property:変数のように読み取れるメソッド @property def celsius(self): return self.__celsius @celsius.setter def celsius(self, value): if value < -273.15: raise ValueError("絶対零度以下にはなれません") self.__celsius = value @property def fahrenheit(self): return self.__celsius * 9 / 5 + 32 @property def kelvin(self): return self.__celsius + 273.15 # 使用:メソッドではなく変数のようにアクセス temp = Temperature(100) print(f"摂氏: {temp.celsius}℃") print(f"華氏: {temp.fahrenheit}°F") print(f"ケルビン: {temp.kelvin}K") temp.celsius = -10 # setter経由で更新 print(f"更新後: {temp.celsius}℃ = {temp.fahrenheit}°F")
摂氏: 100℃ 華氏: 212.0°F ケルビン: 373.15K 更新後: -10℃ = 14.0°F
💡 Pythonのアクセス修飾子:
name → パブリック(外から自由にアクセス可)
_name → 慣例的プライベート(「觸らないで」の意味)
__name → 名前マングリング(クラス外からは_クラス名__nameになる)

🎯 クラスメソッドと静的メソッド

class DateHelper: # クラス変数 WEEKDAYS = ["月", "火", "水", "木", "金", "土", "日"] def __init__(self, year, month, day): self.year = year self.month = month self.day = day @classmethod def from_string(cls, date_str): """文字列から生成するファクトリメソッド""" year, month, day = map(int, date_str.split("-")) return cls(year, month, day) @classmethod def today(cls): from datetime import date d = date.today() return cls(d.year, d.month, d.day) @staticmethod def is_leap_year(year): """クラスやインスタンスに依存しない純粩な関数""" return (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0) def __str__(self): return f"{self.year}年{self.month}月{self.day}日" # classmethodはクラス経由で呼ぶ d1 = DateHelper.from_string("2026-04-19") d2 = DateHelper.today() print(d1, d2) # staticmethodはインスタンスなしで使える print(DateHelper.is_leap_year(2024)) # True print(DateHelper.is_leap_year(2025)) # False
2026年4月19日 2026年4月19日 True False

🏻 実践例:計算ツール設計

silkcodejp.comの計算ツールをクラスで設計すると、こんな感じになります。

class InvestmentCalculator: """積立投賄シミュレーター(iDeCo/NISA対応)""" def __init__(self, monthly_amount, years, annual_rate): self.monthly_amount = monthly_amount self.years = years self.annual_rate = annual_rate / 100 # %→小数 @property def total_contribution(self): return self.monthly_amount * 12 * self.years @property def future_value(self): """複利計算(月次)""" r = self.annual_rate / 12 n = self.years * 12 if r == 0: return self.monthly_amount * n return self.monthly_amount * (((1 + r)**n - 1) / r) @property def profit(self): return self.future_value - self.total_contribution def report(self): return (f"月額: {self.monthly_amount:,}円 × {self.years}年 " f"(年利{self.annual_rate*100:.1f}%)\n" f" 元本: {self.total_contribution:,.0f}円\n" f" 運用益: {self.profit:,.0f}円\n" f" 最終額: {self.future_value:,.0f}円") # iDeCoシミュレーション ideco = InvestmentCalculator(23000, 30, 5.0) print(ideco.report())
月額: 23,000円 × 30年 (年剷5.0%) 元本: 8,280,000円 運用益: 10,637,042円 最終額: 18,917,042円
💡 @propertyを使うメリット:計算ロジックをメソッドに隠蔽しながら、ideco.future_valueのように変数感覚でアクセスできます。値が変わるたびに自動計算されるので、常に最新の値が取得できます。

❓ よくある疲問・ミス

selfを書き忘れた!どんなエラーが出る?

TypeError: speak() takes 0 positional arguments but 1 was given というエラーが出ます。メソッドの第1引数は必ずselfにしましょう。

クラスと関数、どちらを使えばいい?

状態(データ)を持ちながら複数の操作をするならクラス、単純な変換・計算なら関数が適切です。小さな処理にクラスを使うとかえって複雑になります。

継承よりコンポジションを使うべき?

「~は~である(IS-A関係)」なら継承、「~は~を持つ(HAS-A関係)」ならコンポジション(別クラスをインスタンス変数に持つ)がベターです。過度な継承はコードを複雑にします。

dataclassを使うべき場面は?

Python 3.7+では@dataclassデコレータを使うと、__init____repr____eq__が自動生成されます。データを格納するだけのシンプルなクラスに最適です。

まとめ:クラスの5つのポイント
①クラスはデータとメソッドをひとまとめにした「設計図」
__init__で初期化、selfでインスタンス自身を参照
③継承で親のコードを再利用、super()で親の処理を呼ぶ
@propertyでカプセル化しながらスッキりしたAPI
⑤特殊メソッドで演算子やビルトイン関数に対応させる

🎯 学習をさらに深めよう
Python基礎入門ガイド
変数・ループ・関数の基礎から丁寧に解説
記事を読む →