はじめに
クラス初期化の仕組み・役割・よくある誤解までやさしく深掘り
Python を触り始めると、必ず登場するもののひとつが __init__。
でも「コンストラクタってことでしょ?」という理解で止まってしまう人が多いんです。
今回は 本当はどう動いているのか から、実践的な書き方までまとめます。
__init__ とは何か?
結論からいうと、__init__ は インスタンスが生成された後に呼ばれる “初期化関数”。
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
p = Person("Anna", 27)
Person("Anna", 27) が実行された瞬間、
新しい Person インスタンス → __init__ に渡される → 値を設定
という流れで動いています。
インスタンス生成の実際の流れ
Python 内部での正式な流れはこうです:
-
__new__で インスタンスが作られる -
__init__で 作られたインスタンスを初期化する
__init__ は “コンストラクタっぽく見える” けど、正体は 後処理。
こういう裏話、ちょっと面白くない?
self の役割
__init__ の第一引数は必ず self。
これは 「今まさに生成されたインスタンス自身」 を表します。
class Piece:
def __init__(self, kind, pos):
self.kind = kind
self.pos = pos
self.kind とした瞬間、インスタンスが「kind という属性を持つ個体」になります。
Python は constructor 内で self を自分で宣言しないので、
初めて書いた時は「この self どこから来たの?」と混乱した人、多いはず。
return できない理由
__init__ は戻り値を返してはいけません。
返しても Python に完全に無視されます。
def __init__(self):
return "hello" # ← 実はまったく意味がない
理由はシンプルで:
- インスタンスは すでに
__new__によって作られている -
__init__は 返り値を期待する位置にない
デフォルト値や可変オブジェクトの注意点
デフォルト値は使えるが…
class User:
def __init__(self, name, tags=[]): # ← 要注意
self.name = name
self.tags = tags
可変のデフォルト引数(リスト・辞書)は地雷。
すべてのインスタンスで共有されてしまいます。
安全な書き方はこちら:
class User:
def __init__(self, name, tags=None):
self.name = name
self.tags = tags or []
実践:データクラス(@dataclass)なら __init__ 自動生成
Python 3.7+ では @dataclass を使うと __init__ を自動生成できます。
from dataclasses import dataclass
@dataclass
class Piece:
kind: str
pos: tuple
これでもう __init__ は書く必要なし。
値クラス・DTO 的用途に最強。
応用:__new__ を使い分ける例
※かなりレアケースですが、“本物のコンストラクタ” が必要な場面。
class Singleton:
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
こういうとき __init__ より __new__ が主役になります。
まとめ
-
__init__は インスタンス初期化関数(コンストラクタじゃない) - インスタンス自体の生成は
__new__ -
selfはインスタンスそのもの - 戻り値を返しても意味がない
- 可変デフォルト引数に注意
- 値クラスは
@dataclassが便利
Python のクラスは軽くて柔軟だから、
適切に使えるようになると一気にコードが整っていくよ。