Pythonの文字列連結(join)にまつわる議論 まとめと考察(1)
2chの某スレにてPythonRubyのjoin関数の実装の違いについて議論されていましたので、BetaNews的に2chでの議論をまとめつつ、そこに書かれていなかった考察をしてみようと思います。

議論の主題

まず、Pythonにおける典型的なjoinの使い方と多言語のそれをまとめてみましょう。

  • Pythonの場合
    ", ".join(["1", "1", "2", "3", "5", "8"])
  • Rubyの場合
    [1, 2, 3, 5, 8].join(", ")
  • JavaScriptの場合
    ["1", "2", "3", "5", "8"].join(", ")
要約すれば、なぜPythonはbasestring型(str型とunicode型のベース クラス)のメソッドとして「join」が用意されていて、list型のメソッドではないのか、という実装(というか考え方の違い)が議論の的になっていました。

2chでの議論

その問題を指摘したRubyist(っぽい人)のカキコは、「リストを連結するのがstrのメソッドなのはいいのか?」「戻り値が文字列だからといって、要素を連結する機能を文字列に関する機能と考えるほうがどうかしてる」
「joinはあくまで“要素を連結する”ためのメソッド。リストのメソッドで何が悪い」という部分に集約されていました(少なくともこの議論においては)。たしかに、上で例に挙げたPython以外の言語では、配列型に帰属するメソッドとしてjoinが用意されており、「配列をセパレータを使って文字列型にシリアライズする」メソッドとして理解できます。そういったjoinメソッドの理解からは、Pythonの実装は真逆に見えますね。

では、Pythonではなぜそうなっていないのでしょうか。

この問題については、Python本家の言語仕様FAQ(http://www.python.org/doc/faq/general/#why-is-join-a-string-method-instead-of-a-list-or-tuple-method)にも言及されています(読んで字の如く「よくある質問」なのでしょう)。

Pythonの実装として前提となっているのは、

  1. listメソッドはシーケンスを対象とした操作である
  2. シーケンスであるオブジェクトはイテレータである
  3. 文字列はbasestringの派生であり、str、unicode、さらには拡張クラスを実装できる
という条件が考えられます。この1.と2.の条件から、Pythonにおける文字列型のjoinメソッドは、イテレータありさえすれば(文字列型のインスタンスを返すジェネレータであろうとも)、適用可能である、ということになります。実際に、次のようなIDLEの実行結果が得られます。

>>> def foo(txt, minimum = 0, maximum = 0):
while minimum<=maximum:
yield "%s%d" % (txt, minimum)
minimum += 1


>>> ", ".join(foo("test", 1, 10))
'test1, test2, test3, test4, test5, test6, test7, test8, test9, test10'

まとめ風

Pythonにおける自然さ」という意味では、自己書き換え型の(in-place)メソッドであるか否かは、クラス定義の独立性(スケーラビリティ)に依存させるのが自然ということになります。

では、joinの意味付けが統一されていつつ、かつどうやって応用していけばよいのか、次回に書いてみようと思います。