閉じる

Pythonのselfとは何か?クラスとメソッドの基本と使い方

Pythonのクラスで最初に戸惑いやすいのがselfという引数です。

これはインスタンスメソッドの第一引数に現れ、オブジェクト自身を指す参照として働きます。

本記事ではselfが何者で、なぜ必要で、どのように正しく使うかを、初心者向けに段階的に解説します。

エラー例や実行結果も交え、混乱しやすい点を丁寧に整理します。

selfとは何か(Pythonのクラス/メソッド基礎)

selfはインスタンス自身への参照

selfは「そのメソッドが呼ばれているオブジェクト自身」を指す参照です。

これにより、そのオブジェクトの属性や他のメソッドにアクセスできます。

Python
# selfが「自分自身」を指すことを確認する例
class Box:
    def set_value(self, value):
        # インスタンスの属性valueに値を保存
        self.value = value

    def show_ids(self):
        # id()はオブジェクトの「同一性」を表す整数を返します
        print("id(self) =", id(self))

b = Box()
print("id(b)   =", id(b))
b.set_value(42)
b.show_ids()  # selfはbと同じオブジェクトを指す
実行結果
id(b)   = 140630213983600
id(self) = 140630213983600

このようにselfと変数bは同一の実体です。

メソッド中でself.valueと書けば、そのインスタンス固有の値にアクセスできます。

selfはキーワードではない(第一引数の慣習)

selfは予約語ではなく、あくまで「第一引数の名前の慣習」です。

別名でも動きますが、可読性と慣習に従い必ずselfを使いましょう

Python
# 技術的にはself以外の名前でも動くが、非推奨の例
class Counter:
    def inc(this, step=1):  # 動くが慣習に反する
        if not hasattr(this, "n"):
            this.n = 0
        this.n += step

c = Counter()
c.inc()
print(c.n)  # 1
実行結果
1

PEP 8(スタイルガイド)でもインスタンスメソッドの第一引数はselfにすることが示されています。

呼び出しの仕組み: obj.m() -> Class.m(obj)

obj.m()という記法は、実際にはClass.m(obj)という関数呼び出しに変換されます。

インスタンスから属性を取り出すと、関数がバウンドメソッドに変換され、第一引数に自動でobjが渡されます。

Python
class Greeter:
    def hello(self, name):
        print(f"Hello, {name}! I am {id(self)}")

g = Greeter()

# インスタンスから呼ぶ(自動でself=gが渡される)
g.hello("Alice")

# クラスから関数として取り出し、明示的にインスタンスを渡す
Greeter.hello(g, "Bob")

# 取り出したメソッドオブジェクトの中身を観察
bound = g.hello
print("bound.__self__ is g:", bound.__self__ is g)  # バインド先のインスタンス
print("bound.__func__ is Greeter.hello:", bound.__func__ is Greeter.hello)
実行結果
Hello, Alice! I am 140630213983840
Hello, Bob! I am 140630213983840
bound.__self__ is g: True
bound.__func__ is Greeter.hello: True

この変換ルールがあるため、メソッド定義の第一引数が必要になります。

なぜselfが必要か(属性と状態管理)

オブジェクトはそれぞれ独立した状態を持ちます。

selfを介して各インスタンスの状態(属性)を読み書きします。

Python
# 2つのウォレットがお互いに影響しないことを示す
class Wallet:
    def __init__(self, owner, balance=0):
        self.owner = owner       # インスタンス固有の属性
        self.balance = balance   # インスタンス固有の属性

    def deposit(self, amount):
        self.balance += amount   # 自分の残高だけが増える

    def __str__(self):
        return f"{self.owner}: {self.balance}円"

w1 = Wallet("Alice", 1000)
w2 = Wallet("Bob", 500)
w1.deposit(300)
print(w1)  # Alice: 1300円
print(w2)  # Bob: 500円
実行結果
Alice: 1300円
Bob: 500円

w1の操作はw2に影響しません

selfは、この分離された状態管理の要です。

selfとthisの違い(他言語)

JavaやC++のthisは暗黙に渡されますが、Pythonは明示的に第一引数(self)で受け取る哲学です。

これにより、メソッドは「ただの関数」で、束縛の仕組みが透けて見えるため、挙動が理解しやすくなります。

selfの使い方と書き方

__init__とself(初期化と属性定義)

コンストラクタ__init__はインスタンス生成直後に呼ばれ、selfを使って属性を初期化します。

すべてのインスタンス属性は__init__で定義するのが基本です。

Python
class Person:
    def __init__(self, name, age):
        # ここでインスタンス属性を生やす
        self.name = name
        self.age = age

    def greet(self):
        print(f"Hi, I'm {self.name} ({self.age})")

p = Person("Carol", 20)
p.greet()
実行結果
Hi, I'm Carol (20)

インスタンスメソッドの第一引数はself

インスタンスメソッドは定義上最初の引数で自分自身(self)を受け取る必要があります。

これは呼び出し規則(obj.m() → Class.m(obj))の結果です。

Python
class Thermometer:
    def set_celsius(self, c):
        self.c = c

    def as_fahrenheit(self):
        return self.c * 9 / 5 + 32

t = Thermometer()
t.set_celsius(25)
print(t.as_fahrenheit())
実行結果
77.0

属性アクセスはself.が必須

メソッド内でインスタンス属性に触れるときは必ずself.プレフィックスが必要です。

単なるnameはローカル変数を指し、意図した属性にアクセスできません。

Python
class Sample:
    def __init__(self, v):
        self.v = v

    def wrong(self):
        v = 99         # ローカル変数。self.vは変わらない
        return v

    def correct(self):
        self.v = 99    # インスタンス属性を更新する
        return self.v

s = Sample(10)
print(s.wrong(), s.v)   # ローカルだけが99
print(s.correct(), s.v) # 属性が99に更新される
実行結果
99 10
99 99

self.を書かないと別物になる点に注意してください。

selfを省略できない理由

Pythonは「明示は黙示に勝る」という原則で設計されています。

メソッドがどのインスタンスに対して動くかを明示するため、第一引数が必要です。

仮に省略を許すと、関数とメソッドの区別やバインディング挙動が不透明になります。

よくあるエラーと対処(missing ‘self’)

最頻出のエラーはselfを書き忘れることです。

症状と直し方を確認します。

Python
# 典型的なミス: selfを定義し忘れる
class Oops:
    def greet():  # ← selfがない
        print("hello")

x = Oops()
# インスタンスから呼ぶと、Pythonは暗黙にインスタンスを1つ渡す
x.greet()
実行結果
Traceback (most recent call last):
  ...
TypeError: Oops.greet() takes 0 positional arguments but 1 was given

修正は第一引数にselfを追加するだけです。

Python
class Fixed:
    def greet(self):  # ← selfを追加
        print(f"hello from {id(self)}")

Fixed().greet()
実行結果
hello from 140630213985040

selfを使わないメソッドとの違い

@classmethodはclsを受け取る

クラスメソッドはクラス自体を第一引数(cls)に受け取るメソッドです。

ファクトリメソッドなどに向いています。

Python
class User:
    def __init__(self, name):
        self.name = name

    @classmethod
    def from_csv(cls, line):
        # クラスを参照するので、継承先でも正しく動く
        name = line.strip().split(",")[0]
        return cls(name)

u = User.from_csv("dave,admin")
print(isinstance(u, User), u.name)
実行結果
True dave

@staticmethodはself不要

スタティックメソッドはselfもclsも受け取らない、ただの名前空間付き関数です。

ユーティリティ計算などに使います。

Python
class Math:
    @staticmethod
    def add(a, b):
        return a + b

print(Math.add(2, 3))
m = Math()
print(m.add(4, 5))  # インスタンス経由でも呼べるがselfは渡されない
実行結果
5
9

バウンドメソッドとアンバウンドの違い

Python 3では「アンバウンドメソッド」型は存在せず、クラスから取り出したものは単なる関数です。

一方、インスタンスから取り出すとバウンドメソッドになります。

Python
class Echo:
    def say(self, msg):
        print(f"[{id(self)}] {msg}")

e = Echo()

# クラスから取り出す → 関数(未バインド)
func = Echo.say
print(type(func), hasattr(func, "__self__"))

# インスタンスから取り出す → バウンドメソッド
bound = e.say
print(type(bound), hasattr(bound, "__self__"))
print("bound.__self__ is e:", bound.__self__ is e)

# 未バインド関数は自分でインスタンスを渡して呼ぶ
func(e, "manual call")
実行結果
<class 'function'> False
<class 'method'> True
bound.__self__ is e: True
[140630213985808] manual call

この違いを理解すると、obj.m()とClass.m(obj)の等価性が腹落ちします。

selfのベストプラクティス

命名は必ずselfにする

PEP 8に従い第一引数名はselfに統一しましょう。

チーム内で表記が揺れると理解コストが高まります。

短いスコープでもthisやsなどの別名は避けるべきです。

Python
# 良い例
class Good:
    def do(self):  # selfで統一
        pass

# 悪い例(非推奨)
class Bad:
    def do(this):  # チーム開発では混乱のもと
        pass

属性は__init__で定義して一貫性を保つ

すべてのインスタンス属性は__init__で生やすと、どのメソッドからも存在が保証され、補完や型推論にも有利です。

条件分岐の深い場所で唐突にself.x = ...を作るのは避けます。

Python
class Profile:
    def __init__(self, name):
        self.name = name
        self.tags = []       # ここで宣言しておく
        self.active = True   # 既定値もまとめる

    def deactivate(self):
        self.active = False  # 既に存在する属性を更新

Self型ヒント(Python 3.11+)の活用

Python 3.11以降はtyping.Self「自分自身を返す」型を表現できます。

流れるようなAPI(メソッドチェーン)の型安全性が向上します。

Python
# Python 3.11+
from typing import Self

class Builder:
    def __init__(self):
        self.parts: list[str] = []

    def add(self, part: str) -> Self:
        self.parts.append(part)
        return self  # Selfに適合する(同じインスタンスを返す)

    def build(self) -> str:
        return "-".join(self.parts)

b = Builder().add("A").add("B")
print(b.build())
実行結果
A-B

Selfは継承関係でも正しく派生型を推論するため、メソッドチェーンを多用する設計で特に効果があります。

以下に3種のメソッドの要点を整理します。

種類第一引数目的呼び出し例
インスタンスメソッドself個々のオブジェクトの状態を操作obj.m(), Class.m(obj)
クラスメソッドclsクラス全体に関わる処理、代替コンストラクタClass.m(), SubClass.m()
スタティックメソッドなし補助的な独立処理(名前空間として提供)Class.m(), obj.m()

「状態を触るならself」「クラスに紐づく操作はcls」「どちらにも触れないならstaticmethod」と覚えると迷いにくいです。

まとめ

selfはインスタンス自身への明示的な参照であり、obj.m() → Class.m(obj)という呼び出し変換のもとに機能します。

属性へのアクセスや状態管理にはself.が必須で、第一引数の書き忘れは典型的なTypeErrorの原因です。

classmethodとstaticmethodとの差も理解し、PEP 8に従って第一引数名はselfで統一してください。

Python 3.11以降はtyping.Selfでメソッドチェーンの型安全性を高められます。

以上を押さえれば、クラス設計とメソッド定義が一層明快になり、実践で迷う場面が大きく減ります。

この記事を書いた人
エーテリア編集部
エーテリア編集部

人気のPythonを初めて学ぶ方向けに、文法の基本から小さな自動化まで、実際に手を動かして理解できる記事を書いています。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!