メソッドの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の暗黙より明示というポリシーから今の結論に達したのかもしれない

〜使い

一覧表が見つからなかったのでまとめてみた。ご指摘大歓迎。

Python Pythonista
Ruby Rubyist
Perl Perl Monger
PHP PHPer (ぺちぱー)
Scheme Schemer
Lisp Lisper
ML MLer
Java Javanese
C# C#er (C Sharper)
C C屋、Cげんがー
C++ C++屋、Cぷらぷらー
Delphi Delphian
Smalltalk Smalltalker
OCaml Caml rider
Haskell Haskeller
Scala ?
Erlang ?
Prolog ?
JavaScript JavaScripter
Brainfuck ?


Java、C#、C++なども呼び名があるべき。呼び名がないのは言語が愛されていない証拠?

OSその他も追加してみた。他の呼び方もいろいろありそうだが、自分のメモ代わりに一般的っぽいのを書いてみた。

Mac マカー
Windows ドザ
Linux リナー
Tex TeXニシャン(テフニシャン)

メモ

「ときどきの雑記帖 i戦士篇」日記経由でメモ。あとで読む。


以下、Python版の実装。出典元は省略。

def qsort(L):
    if len(L) <= 1: return L
    return qsort([lt for lt in L[1:] if lt < L[0]]) + L[0:1] + \
           qsort([ge for ge in L[1:] if ge >= L[0]])
  • エラトステネスのふるい
from itertools import ifilter, count
def sieve():
    g = count(2)
    while True:
        prime = g.next()
        yield prime
        g = ifilter(lambda x, prime=prime: x%prime, g)


Pythonさん、無限リストの取り扱いをもっと一般化してください。