前の記事では、同じ名前が2回出てくるパターンを2つ見ました。
-
calendar.calendar→.の左がモジュール、右が関数 -
json.dump(fp=fp)→=の左が引数名、右が自分の変数
今回は、self.name = name について考えます。
self.name = name # nameが2回!?
今回も2回出てくるパターンですので、左右それぞれどういう意味か整理します。
①②の記事でやったクラスの話と直接繋がる内容ですので、
少し被るところもあるかもしれませんが、復習がてらに深掘りしていきます。
self.name = name はどこで出てくるの?
例えば、こういうコードの中に出てきます。
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
また __init__ が登場です。どこかの記事でちょっと難癖をつけた気がしますが、
少し見慣れてきて、なんとも思わなくなってきました。世の中慣れということです。
そしてこれは「インスタンスが生まれた瞬間に走る初期設定」でしたよね。
今回はクッキーではなく人物を例えにして、これらのコードを考えてみます。
左右の違いを整理する
self.name = name
# 左:このPersonが自分専用で持つデータ
# 右:外から渡されたデータ
右の name は、外から渡されたデータです。
person = Person("田中", 28)
# ↑ この "田中" が name として受け取られる
左の self.name は、「このPersonが自分専用で持つデータ」です。
以前の記事で出てきた self.shape と全く同じ構造です。
- 右側の
nameは、その場限りの「引数(ひきすう)」 - 左側の
self.nameは、ずっと保持される「インスタンス変数」
【イメージ訳】「いま届いた名前(name)を、自分専用の倉庫(self.name)に保管せよ!」
そもそも self って何者?
①②の記事で「self がなぜここに出てくるのか、詳しくは別の機会に……!」と先送りにしていました。
その別の機会を今とします。
さて、self は、「今まさに操作しているインスタンス自身」 を指す言葉です。
英語のセルフは、セルフサービスだとかのイメージなので、自分自身を指すっていうのはなんとなくイメージがつかみやすいですよね。
self と書くだけで、Pythonがその時々に合わせて person1 や person2 に読み替えてくれます。魔法の代名詞!といったところでしょうか。だから同じ self.name というコードでも、中身が混ざらないのです。
person = Person("田中", 28) と書いたとき、Pythonの裏側ではこんなことが起きています。
# 自分が書くコード
person = Person("田中", 28)
# Pythonが裏側でやっていること(イメージ)
Person.__init__(person, "田中", 28)
# ↑
# 「今作ったこのインスタンス」を
# self として渡している
つまり self は「今まさに操作しているこの人物(インスタンス)のこと」を指しています。Pythonが自動で渡してくれるので、自分で self に何かを入れる必要はありません。
だから __init__(self, name, age) の self は「このインスタンス自身を受け取る席」で、name や age とは役割が違います。
def __init__(self, name, age):
# ↑ ↑ ↑
# 自分自身 渡された 渡された
# 名前 年齢
少しはselfがわかってくるでしょうか。
私はここでまた少し混乱しています。
なぜわざわざ同じ名前にするの?
皆さんやはり思いますよね?
「name と name、同じ名前にしやがって」と。
(そう思うのはやはり私だけなのでしょうか?)
これは以前の内容と全く同じです。
違う名前にしすぎると「結局どの変数に入るんだっけ?」と迷子になってしまうから。
「name(名前)というデータは、どこまで行っても name と呼ぶのが一番シンプル!」という、ある種の割り切りがこの書き方を生んでいるんですね。
もういい加減に納得することにしました(表面上は)
class Person:
def __init__(self, nickname): # 引数名を nickname にした
self.name = nickname # self.name には nickname の中身が入る
person = Person("田中")
print(person.name) # → 田中(ちゃんと動く)
右側(引数名)は自由。左側(インスタンス変数名)はあとで読み出すときと合わせる必要があります。②の記事でやった self.shape = mold と全く同じですね。
複数の属性を持つと何が嬉しいの?
クッキーのときは shape(形)だけでしたが、今回は name と age の2つがあります。
応用編として、少し考えてみます。
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
person1 = Person("田中", 28)
person2 = Person("鈴木", 34)
print(person1.name) # → 田中
print(person1.age) # → 28
print(person2.name) # → 鈴木
print(person2.age) # → 34
person1 と person2 は同じ Person という型紙から作られていますが、それぞれ自分専用のデータを持っているので、混ざることはありません。
クッキーのときと全く同じ構造ですよね。型紙が変わっても、仕組みは一緒です。
①〜④を通じて
calendar.calendar(2026) # ドットの左右で見る → 左がモジュール、右が関数
json.dump(fp=fp) # = の左右で見る → 左が引数名、右が自分の変数
self.name = name # self. があるかどうかで見る → 左がインスタンス変数、右が引数
全部に共通しているのはこれです。
「名前が同じ=同じ意味」ではない。どの位置に書かれているかで役割が決まる。
「同じ名前が2回出てくる」現象は、Pythonが私たちをいじめているわけではなく、「情報の通り道を整理している跡」 だったんです。
- どこから来たデータか?(引数、モジュール)
- どこに置くデータか?(インスタンス変数、関数)
この「入口」と「出口」がたまたま同じ名前なだけ。
これが分かれば、もうコードの中で迷子になることはありません!
……たぶん。私はまだ混乱しますが。
まとめ
| 疑問 | 答え |
|---|---|
self.name と name の違いは? |
左はインスタンス変数、右は引数。役割が全然違う。 |
self って何者? |
今まさに操作しているインスタンス自身。 |
| なぜ同じ名前を使うの? | 違う名前にすると今度は迷子になるから。割り切り。 |
| 見分け方は? |
self. がついているかどうかで判断する |
①〜④を通じて「同じ名前が2回出てくる」パターンが整理できていたら嬉しいです!
まだまだ初学者なので、間違っているところがあればぜひご指摘いただけますと幸いです。