読者です 読者をやめる 読者になる 読者になる

akiyoko blog

akiyoko の IT技術系ブログです

Pythonのself

Pythonでインスタンスメソッドというのは,インスタンスにバウンド(ヒモ付?束縛?)されている関数に過ぎない

http://blog.livedoor.jp/odaxsen/archives/1596542.html

これまでぼんやりとしか理解できていなかった「Pythonのself」について、上のエントリーを読んで、一気に閃いた感じがしたのでちょっと書いてみます。


閃いたというのは、これ。

selfは、変数や関数をインスタンスに「バインド」させる。


テストしてみました。



変数とself

以下の例では、2行目の a はクラス変数、4行目の a はローカル変数(両者は別物)。
いずれもインスタンスにはバインドされていない。

>>> class Hoge(object):
...     a = 1
...     def __init__(self):
...         a = 2    # メソッド内のローカル変数と見なされる
...
>>> Hoge.a
1
>>> hoge = Hoge()
>>> hoge.a    # self.a を参照するのと同じ(詳細は後述)
1
>>> hoge.__dict()__    # インスタンスにバインドされていない
{}

ちなみに、__dict__ は「オブジェクトの(書き込み可能な)属性を保持する辞書オブジェクト」なので、インスタンス変数を表示することができる。


次の例では、2行目の a はクラス変数、4行目の a はインスタンス変数(両者は別物)。
4行目の変数 a がインスタンスにバインドされている。

>>> class Hoge(object):
...     a = 1
...     def __init__(self):
...         self.a = 2    # 「self」を使うとインスタンス変数になる
...
>>> Hoge.a
1
>>> hoge = Hoge()
>>> hoge.a
2
>>> hoge.__dict__()    # インスタンスにバインドされた
{'a': 2}


ここで、変数は「Pythonのselfという機構」によって、インスタンスにバインドされると考えるとスッキリすると思う。


Python言語リファレンスには、

インスタンス変数を作成するには、メソッドの中で self.name = value でセットできます。

とあるようです。なるほど、納得。


ここまで理解できたところで、ちょっと混乱しがちなパターン。

>>> class Hoge(object):
...     a = 1
...     def __init__(self):
...         print self.a
...         self.a = 2
...         print self.a
...
>>> Hoge.a
1
>>> hoge = Hoge()    # あれ、4行目でエラーじゃないの!?
1
2
>>> hoge.a
2

しばらく悩みましたよ、これ。
これを理解するにはもう一つのルールを知らないといけないようです。

クラス変数もインスタンス変数も “self.name” 表記でアクセスすることができます。この表記でアクセスする場合、インスタンス変数は同名のクラス変数を隠蔽します。

ほう。つまり、“self.name”という表記によって、クラス変数もインスタンス変数も参照できるけれども、同名のクラス変数とインスタンス変数があった場合は、インスタンス変数を優先して参照しますよ、と。
上の例では、4行目の self.a は、2行目のクラス変数を参照していることになります。5行目では、self を使って新たにインスタンス変数 a が生成される(「a」という同名のクラス変数とインスタンス変数が存在することになる)ので、6行目の self.a は、5行目で生成したインスタンス変数が優先されて参照される、というわけですね。

http://d.hatena.ne.jp/aroma_black/20110419/1303222363



関数とself

インスタンスメソッドの場合も同じ。メソッドの第1引数の self が、関数をインスタンスにバインドする役割を果たしていると考えれば分かりやすいんじゃかないかなぁ。

>>> class Hoge(object):
...     # selfを第1引数にするとインスタンスメソッドと見なされる
...     def aaa(self):
...         return 1
...     def bbb():
...         return 2
...
>>> hoge = Hoge()
>>> hoge.aaa()
1
>>> hoge.bbb()  # インスタンスメソッドとして構文的にNG
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: bbb() takes no arguments (1 given)


ちなみに、bbb は im_func を使うと、関数として呼び出すことができます。「ユーザ定義メソッド」と呼ばれています。まぁ、実際に使うことはなさそうですが。

(続き)
>>> Hoge.bbb.im_func()
2

 

まとめ

  1. self を使うと、変数を(インスタンス変数として)インスタンスにバインドできる。
  2. self を使うと、関数を(インスタンスメソッドとして)インスタンスにバインドできる。
  3. “self.name” という表記でも、クラス変数を参照できる(更新は不可)。