はじめに
Pythonを学習していて、クラスやインスタンスメソッドが出てきたときに、少し混乱したことがあります。
それが self です。
インスタンス変数には self をつけるのに、メソッドの引数にはつけない。
「今回の引数にも self が必要なのでは?」と考えてしまいました。
さらに、インスタンス変数やメソッド、仮引数、実引数といった用語も混ざってしまい、何が何だか分からなくなってきました。
今回は、スマホのバッテリー残量を確認する例で整理してみます。
1. まずはクラスとインスタンス
スマホを表すクラスを作成します。
class Smartphone:
pass
phone = Smartphone()
Smartphone はクラスです。
クラスは、インスタンスを作るための設計図のようなものです。
そして、
phone = Smartphone()
によって作られた phone がインスタンスです。
ここで、まず次の2つを区別します。
| 用語 | 意味 |
|---|---|
| クラス | インスタンスを作るための設計図 |
| インスタンス | クラスから作られた実体 |
2. インスタンス変数とは?
次に、スマホの名前とバッテリー残量を設定します。
phone.name = 'My Phone'
phone.battery = 80
この時点で、phone というインスタンスは次のデータを持っています。
-
name:My Phone -
battery:80
このように、インスタンスが持っているデータをインスタンス変数(属性)と呼びます。
ここで注意したいのは、インスタンス変数は関数やメソッドそのものではないということです。
データと処理は別のものです。
3. インスタンスメソッドとは?
次に、バッテリー残量を確認する処理を作ります。
class Smartphone:
def show_battery(self):
return self.battery
この show_battery がインスタンスメソッドです。
インスタンスに関係する処理を、クラスの中に定義しています。
ここで登場するのが self です。
self は、メソッドを呼び出したインスタンス自身を表します。
例えば、
phone.show_battery()
と呼び出すと、self には phone が渡されます。
そのため、
self.battery
は、phone.battery を参照することになります。
つまり、インスタンスが持っているバッテリー残量を取得しているわけです。
4. self と引数の違い
今度は、バッテリー残量が一定の値を下回っているか確認するメソッドを作ります。
class Smartphone:
def check_battery(self, limit):
if self.battery < limit:
return '充電してください'
else:
return 'バッテリーは十分です'
ここでは、self と limit が登場します。
それぞれの役割を整理します。
| 名前 | 役割 |
|---|---|
self |
メソッドを呼び出したインスタンス自身 |
limit |
メソッドを呼び出すときに渡す値 |
self.battery |
インスタンスが持っているバッテリー残量 |
例えば、次のように呼び出します。
phone.check_battery(30)
このとき、
-
self→phone -
limit→30
が入ります。
self はPythonが自動的に渡してくれるため、呼び出し側では書きません。
5. 仮引数と実引数とは?
ここで、引数に関する用語も整理します。
def check_battery(self, limit):
このメソッド定義側にある self と limit は仮引数です。
一方、
phone.check_battery(30)
この呼び出し側で渡している 30 は実引数です。
| 用語 | 意味 | 今回の例 |
|---|---|---|
| 仮引数 | 値を受け取る側の名前 | limit |
| 実引数 | 実際に渡す値 | 30 |
つまり、仮引数は受け取る側、実引数は渡す側です。
なお、self もメソッド定義上の仮引数です。
ただし、インスタンスメソッドを呼び出すとき、Pythonがインスタンス自身を自動的に渡すため、呼び出し側では self を書かなくてよいのです。
6. なぜ limit に self がいらないのか?
ここが今回、一番混乱したポイントです。
self.battery は、インスタンスが持っているデータです。
一方、limit は、メソッドを呼び出すときに渡す値です。
例えば、
phone.check_battery(30)
phone.check_battery(50)
phone.check_battery(70)
同じインスタンスに対して、異なる値を渡すことができます。
そのため、limit はインスタンスの持ち物ではなく、今回の処理で受け取る値として扱います。
ここで、次のように考えると整理しやすくなりました。
- インスタンスが持っているデータを参照するときは
self.属性名 - メソッドを呼び出すときに渡した値は、引数名だけで参照する
7. 実行してみる
ここまでの内容をまとめて、実行してみます。
class Smartphone:
def check_battery(self, limit):
if self.battery < limit:
return '充電してください'
else:
return 'バッテリーは十分です'
phone = Smartphone()
phone.name = 'My Phone'
phone.battery = 20
result = phone.check_battery(30)
print(result)
出力結果:
充電してください
phone.battery は20、呼び出し時に渡した limit は30です。
20は30より小さいため、「充電してください」が返ってきます。
8. 用語をまとめて整理する
最後に、今回登場した用語を整理します。
| 用語 | 意味 | 今回の例 |
|---|---|---|
| クラス | インスタンスを作る設計図 | Smartphone |
| インスタンス | クラスから作られた実体 | phone |
| インスタンス変数 | インスタンスが持つデータ | phone.battery |
| インスタンスメソッド | インスタンスに関係する処理 | check_battery() |
self |
メソッドを呼び出したインスタンス自身 | phone |
| 仮引数 | 値を受け取る側の名前 | limit |
| 実引数 | 実際に渡す値 | 30 |
まとめ
今回の学習で整理できたことは、次の3つです。
- インスタンス変数は、インスタンスが持っているデータ。
- インスタンスメソッドは、インスタンスに関係する処理。
- 仮引数は受け取る側、実引数は渡す側。
そして、self が必要か迷ったときは、
「これはインスタンスが持っているデータなのか?それとも今回渡された値なのか?」
と考えることが大切だと感じました。
まだ学習中ですが、実際にコードを書き、デバッグしながら少しずつ理解を深めていきたいと思います。