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はプログラマにとって「自分に厳しい」言語と言えるかもしれない。