メソッドのself

最近『初めてのRuby』を読んでいる。Pythonのメソッドの第一引数にselfを書かなくてはいけないが、Rubyと対比して理由がなんとなく分かったので検討してみる。


まずPythonではRubyと同様、インスタンスメソッドとトップレベル関数の区別がない。Pythonではメソッドと関数という呼び方をする。関数およびメソッドは単なるオブジェクトの属性である。つまりメソッドはクラスオブジェクトのcallableな属性、関数はモジュールオブジェクトのcallableな属性である。Pythonでも全てがオブジェクトなので、この区別をしないということが重要となる。


ここで、以下のクラス定義を考えてみる。

>>> class Foo:
...   a = 1
...   def foo(self):
...     a       # エラー (1)
...     Foo.a   # OK     (2)
...     self.a  # OK     (3)
...

クラス内に関数を定義した場合、ローカル変数(グローバル変数)、クラス変数、インスタンス変数の区別を考えなくてはならない。Rubyでは変数の頭に「何も付けない」、「@を付ける」、「@@を付ける」で構文上区別している。Pythonでは以下のようになる。


1. クラス内で関数を定義すると属性になるので、クラス内で変数を定義しても属性とする

    • 属性なので、Foo.a のような構文で参照させる


2. 次にローカル変数(グローバル変数)とそれ以外のクラス変数、インスタンス変数を区別するため以下のようにする

    • クラス変数は、(2)のようにクラス名を先頭に付ける (ここで、属性である Foo.a をクラス変数と考えることができる)
    • ローカル変数(グローバル変数)は先頭に何も付けない (これは、他と一貫性があり当然である)
    • インスタンス変数は、(3)のようにselfを先頭に付ける (ここで、クラス変数とローカル変数以外の構文を用いた参照方法が必要となるのでselfを導入する)


3. selfをどうにかしないといけない

    • しかし、Pythonではメソッドと関数の区別がない。メソッド「定義」だけ特殊にしたくない
    • そこで、メソッド(となるべき関数)では「第一引数に自分自身のオブジェクトを表すselfという引数を付ける」という決まりを作る (selfという名前でなくても良い)
    • すると、定義するときにメソッドと関数の区別をする必要がない
    • しかし、メソッド呼び出しの度に第一引数にインスタンスを渡すのは面倒なので、インスタンスからクラス内の関数を参照した場合、第一引数に自動的にインスタンスオブジェクトが渡されてカリー化されたメソッドに自動的に置き換わるという仕組みになっている


ここで、ポイントとしてはRubyのようにselfは省略可とした場合、インスタンス変数に特殊な構文を用意しない限り、ローカル変数とインスタンス変数の区別がつかなくなってしまう。構文上区別しなくても良いが、lookupの規則が複雑になり遅くなる。また同じ変数名が使用できないという制限も付いてしまう。なので通常は違うものであるとして区別したいので、selfを導入するしかない。


4. Rubyのような暗黙のselfはどうだったのか?

    • これはOKだったかもしれない。参照するときは self.a、定義時に第一引数としては渡さない
    • しかし、トップレベル関数はselfがモジュールオブジェクトとなるが、クラスとモジュールでは属性と変数の扱いが若干異なる
    • また、Pythonの暗黙より明示というポリシーから今の結論に達したのかもしれない