の名前バインディング

Pythonは(ほかのスクリプト言語と同じく)、実行時に名前のバインディングを解決する。変数名、クラス名、メソッド名などいずれも実行時に存在していれば問題なく、静的チェックはない。

もちろんとても便利な特徴なのだが、油断していると以下のようなコードを書けてしまう。

class A:
    def f1(self):
        self.f2()

class B(A):
    def f2(self):
        ...

つまりスーパークラスからサブクラスのメソッドを呼び出して何の問題もない。これが便利ということもあるかもしれないが、設計としてはおかしいし、コードを読むほうも混乱する。Aのインスタンスでf2()を呼ぶと、もちろん実行時エラー(「AttributeError: A instance has no attribute 'f2'」)になる。

これは極端な例だが、依存関係をループにしてしまいやすいという問題は、クラスの継承に限らない。複数のモジュール(って言っていいのかな。ソースファイルのことです)に分割して開発していると、モジュールの依存関係に同じようなことが起きる。

例として、掲示板を作っているとする。掲示板上のコンテンツ関連の処理を1つのモジュールで開発していたが、掲示板上の画像についての処理が増えてきたので、その部分を別モジュールに分離したい。しかし、いくつかのヘルパー的なサブルーチンがあって、テキストと画像の両方に絡み合った処理をしている。

Javaであれば、絡み合った部分をきちんと解きほぐして、適切なインターフェースを定義するだろう。そうしないと実装できない。ヘルパーだけを別クラスにくくり出すということも検討するかもしれない。

しかしPythonであれば、画像関連の処理を別モジュールにしてしまっても、従来のヘルパーはそのまま機能する。実行時にはすべての必要なモジュールが読み込まれていて、動的名前解決で呼び出せるからである。

もちろんPythonでもきちんと対応することは可能だ。ただし、そのときには「コンパイルエラーが出なくなるまで直す」ことはできないし、「既存のテストがすべてグリーンになるまで直す」という手も使えない。「不吉なにおいがしなくなるまで直す」というやりかたが適切に思える。

Pythonは(ほかのスクリプト言語と同じく)、すばやく実装して動かすのが容易だが、discipline(規律・規約)を徹底する能力はJavaなどに劣る。そのことを意識していないと、思わぬ墓穴を掘ることになる。意思の力なりほかのツールなりを使って、不足を補填することを考えるべきだ。そういう意味では、Pythonプログラマにとって「自分に厳しい」言語と言えるかもしれない。