nikkie-ftnextの日記

イベントレポートや読書メモを発信

Pythonの組み込み型にlenメソッドはなくて、長さを知るときにlen関数を使うのはなぜ? Guido氏の意向に迫る

Pythonは歴史が長いので、色々な方がその理由を解説されていますが、私も自分の言葉で説明したくなったので一本書きます。

その理由は、生みの親(かつての慈悲深き終身独裁者Guido氏の意向です。
Guido氏の考えを覗きに行きましょう。

目次

デザインと歴史 FAQ

公式ドキュメントの中にはFAQのページがあります。
その中に表題の疑問にズバリな項があります!

Python にメソッドを使う機能 (list.index() 等) と関数を使う機能 (len(list) 等) があるのはなぜですか?

なんでlist.index()はある1のに、list.len()がないんですか!!

この項目は翻訳があるので日本語で読めます。
それを読んでもいいのですが、リンクされているGuido氏のメールを覗いてみましょう(一次情報ですからね)

Guido氏のメール

このメールはlist.len()メソッドに反対するものです。
2つのPython rationaleを持って説明しています

1つ目:Human Computer Interaction(HCI)から

2つの理由が説明されます。
この2つは結びついている(intertwined)とのことでした

(a) For some operations, prefix notation just reads better than postfix

(a)についてはFAQにそのまま掲載されていそうです

(a) 幾つかの演算では、接頭辞は接尾辞よりも単純に読みやすいからです。

ふむふむ、len(list)(接頭辞の書き方)の方が、list.len()(接尾時の書き方)よりも読みやすい

(b) When I read code that says len(x) I *know* that it is asking for the length of something.

(b)もFAQに掲載されていそうでした。

(b)の理由は人間にとっての分かりやすさと理解しました。
自分の理解のために構造化しました(メールにない情報はIMOで補っています)

  • len(x)というコードは、なにか(x)の長さを尋ねているコード
    • 結果は整数
    • 引数のxはある種のcontainer
      • IMO:このxはcontainerということ以外は分からなくてよい
  • x.len()というコード
    • xについて事前知識が必要になっていると言っている
    • どんな事前知識?
      • xは、len()メソッドが標準なインターフェースを実装したクラスのインスタンス
      • または、xは、len()メソッドが標準なクラスを継承したクラスのインスタンス
      • IMO:まとめると、xのクラスがどんなクラスかという事前知識

(b)に続く段落は、言い換えです(ここから下はFAQにはありません)

Saying the same thing in another way, I see 'len' as a built-in *operation*.

Guido氏は'len'を組み込みの操作として見ているとのこと。
ここはかなり強い想いがあるようで、メソッドにしたいという意見にも配慮しつつ、

'def len(self): ...' certainly sounds like you want to demote it to an ordinary method.

'def len(self): ...'とメソッドにする案は、lenという操作を通常のメソッドに格下げしたいように聞こえる。

Guido氏としては、長さを尋ねる操作をメソッドよりも上位のレベルで捉えていることが分かりました。
これは私の感想ですが、メソッドよりも上位の操作は、+などの演算子for hoge in foo:のような制御構造と同じように見るということかなと思いました

2つ目:特殊メソッドの命名規約から

もう1つの理由にいきましょう。

特殊メソッドとは、用語集によると

ある型に特定の操作、例えば加算をするために Python から暗黙に呼び出されるメソッド。
メソッド名の最初と最後にアンダースコア 2 つがついています。

このアンダースコアを2つ付ける(Python界隈では「ダンダー」と呼ばれる)命名規則に関係した理由でした。

  • なぜ特殊メソッドの名前を単にspecialではなく__special__としたのか
  • Guido氏は特殊メソッドが対応する操作を、クラスでoverrideすることを見越している
  • __special__という特殊メソッド名であれば、通常のメソッド名とは異なるのでユーザが誤って意図せずにoverrideすることがない
    • specialという特殊メソッド名だと、すべての特殊メソッド名を頭に入れない限り、不幸なことに、誤って意図せずにoverrideしてしまう

思うに、__special__という特殊メソッド名によって、「正しい使い方を簡単に」を実現したのではないでしょうか?

  • Pythonを始めた人
    • 「長さはlen()関数」とだけ覚えればよい
    • クラスにlen()メソッドを定義しても、何も影響はない
  • Pythonに習熟した人
    • 特殊メソッド__len__()の存在に気づき、定義したクラスの振る舞いをカスタマイズできる

参考文献(先人に感謝)

Pythonコミッターの稲田さんのブログ2

「サイズを取るメソッドは.length()」という 暗黙のルールができてしまい、そのルールが頭に入っていない人が一貫性の無いコードを書いてしまう。

Pythonは、何かのルールに準拠するためのメソッド(以降、特殊メソッド)は「__???__」という形にする、という一般ルールをつくり、それを interface の代わりにした。

__len__ に対するインタフェースは len() という組み込み関数になっている。

Python Distilled』 7.6より

(※Pythonicという言葉は)通常はオブジェクトがPythonの他の部分とうまく調和していることを指します。
これは () Pythonの核となる機能を合理的な範囲でサポートすることを意味します。
大半の場合、() あらかじめ定義された特殊メソッドをクラスに実装することでPythonicとなります。

Guido氏の意向の理解まとめ

nikkieの言葉で再構成したまとめです。

  • 長さを尋ねる操作は、メソッドよりも上位のレベルで捉えている
  • これらの上位レベルの操作はPython言語は特殊メソッドで提供
    • 特殊メソッドをPython処理系が暗黙に呼び出す仕組み
  • 特殊メソッドの命名規則__special__(アンダースコア2つで挟む)
    • このルールを知らなくても大きな過ちを犯すことはない(フールプルーフ
      • アンダースコアがない命名規則だったら、特殊メソッドを意図せずに誤って上書きがありえる
      • この命名規則を知らないにも関わらず、特殊メソッドを意図せずに誤って上書きする事象はほとんど起こらなそう
    • このルールを知ったPython使いは使い倒せばよい
      • 使い倒すことで、ユーザが定義するクラスがPythonicとなる

気づいたのですが、この意向によりPythonという言語は、習熟度に応じた棲み分けが実現できているんですね!

  • 入門時
    • 長さはlen()関数、加算は+、反復はfor文、...
    • 特殊メソッドを一切知る必要がない
  • 慣れてきたら
    • ようこそ、特殊メソッド(表現力豊かなコードが書ける!)

P.S. Pythonに飼い慣らされたnikkieは

他の言語を使うときに「Pythonでいうlen()関数はないの?」となっていつも洗礼を浴びます。
例えばKotlinですが、

「郷に入っては郷に従え」だなと思っています。