所詮selfなんてそんなもの

python初学者が多分かなり初期から違和感を感じるであろうところのものであるよ、な。

モジュールレベル関数で書き始めたとするね:

1 def fun1(ini, s):
2     return ini + s
3 
4 def fun2(ini, s):
5     return fun1(ini, s) * 2
6 
7 print(fun2(3, 5))  # => 16

iniを必ず渡すのだから、と、class にしたくなるわけだ:

 1 class Fun(object):
 2     def __init__(self, ini):
 3         self.ini = ini
 4 
 5     def fun1(self, s):
 6         return self.ini + s
 7 
 8     def fun2(self, s):
 9         return self.fun1(s) * 2
10 
11 print(Fun(3).fun2(5))  # => 16

いきなりこの完成形だけ見せられるから、「なんで self を受け取るの、渡しもしないのに」と思うわけだ。

書き換え作業中、を考えてみれば腑に落ちたりする人もいるかもな:

 1 # -*- coding: utf-8 -*-
 2 class Fun(object):
 3     def fun1(self, s):
 4         return self.ini + s
 5 
 6 def fun2(self, s):
 7     return self.fun1(s) * 2
 8 
 9 self = Fun()  # 「あなたの」完成プログラムでは真似しないでね
10 self.ini = 3
11 print(fun2(self, 5))  # => 16

現実にこれに似た作業をすることもあるし、「脳内では」これが起こってることも多いかな。

ここからは想像だけれど、メソッドが self を受け取るように記述させる言語仕様に違和感を感じる人は、C 未経験者もしくは C に不慣れな人たちで、違和感を感じない一群はきっと C に慣れている人たちだ。

現実にも Python C API はこんななのよ:

1 static int
2 array_resize(arrayobject *self, Py_ssize_t newsize)

「所詮 self なんて」の話でもあるけれど、「所詮オブジェクト指向なんて」でもあって、オブジェクト指向の原点はなんら深淵なものなんかなくて、

1 #include <stdio.h>
2 
3 /* ... */
4 
5     FILE* fp = fopen("aaa.txt", "rb");
6     while (fgets(buf, 256, fp)) {
7         /* ... */
8     }
9     fclose(fp);

「おんなじ fp を必ず渡すんだから…」と FILE で「串刺し」にしたかった、てことだけなんであるし、今だって別にそこからとてつもなく飛躍しとるわけでもない。…、というか、この最もプリミティブな「オブジェクト指向」から掛け離れた深淵なものと考えてしまうから、間違った設計がはびこるわけだね。「継承による差分プログラミングで工数削減」とかさ。どんなに優秀な書籍が「継承はそれでしか書けない場合以外は避けよ/リスコフの代入規則に従わない継承は誤った継承である/下向きのキャストが必要になる設計はアホ」と繰り返し伝えても、「継承でしかプログラムを書けない」技術者は後を絶たない。あーぁ。

あとさ。「オブジェクト指向は大事な道具」ではあるけれど、「オブジェクト指向だけが大事な道具」ってのがアホなんだぞ。不可欠ですよ、オブジェクト指向。大抵の言語では。けどそれだけが道具、なわけでもないよ。