web-dev-qa-db-ja.com

Python join:なぜlist.join(string)ではなくstring.join(list)なのですか?

これはいつも私を混乱させています。これはより良いように思えます:

my_list = ["Hello", "world"]
print my_list.join("-")
# Produce: "Hello-world"

これより:

my_list = ["Hello", "world"]
print "-".join(my_list)
# Produce: "Hello-world"

このような特別な理由はありますか?

1567
Evan Fosmark

これは、リストだけでなく、あらゆるイテラブルを結合できるからです。しかし、結果と "joiner"は常に文字列です。

例えば:

import urllib2
print '\n############\n'.join(
    urllib2.urlopen('http://data.stackexchange.com/users/7095'))
1156
recursive

これはPython-Devの Stringメソッド...やっと threadで議論され、Guidoに受け入れられました。このスレッドは1999年6月に始まり、str.joinは2000年9月にリリースされた(そしてUnicodeをサポートした)Python 1.6に含まれました。 Python 2.0(strを含むjoinメソッドをサポート)は2000年10月にリリースされました。

  • このスレッドで提案されている4つのオプションがありました:
    • str.join(seq)
    • seq.join(str)
    • seq.reduce(str)
    • 組み込み関数としてのjoin
  • Guidoはlists、Tuplesだけでなく、すべてのシーケンス/イテラブルをサポートしたいと考えていました。
  • seq.reduce(str)は初心者にとって難しいです。
  • seq.join(str)はシーケンスからstr/unicodeへの予期しない依存性をもたらします。
  • 組み込み関数としてのjoin()は特定のデータ型のみをサポートします。そのため、組み込みの名前空間を使用するのは良くありません。 join()が多くのデータ型をサポートしている場合、最適化された実装を作成するのは難しいでしょう。もし__add__メソッドを使って実装されているならO(n²)です。
  • 区切り文字列(sep)は省略しないでください。明示的は暗黙的より優れています。

このスレッドに他の理由はありません。

ここにいくつかの追加の考えがあります(私自身、そして私の友人のもの)。

  • Unicodeのサポートは来ましたが、それは最終的なものではありませんでした。当時はUTF-8が最もUCS2/4に代わるものでした。 UTF-8文字列の合計バッファ長を計算するには、文字コード規則を知る必要があります。
  • 当時、Pythonは、ユーザーがシーケンスのような(反復可能な)クラスを作成できる共通のシーケンスインターフェース規則を既に決めていました。しかし、Pythonは2.2まで組み込み型の拡張をサポートしていませんでした。当時、基本的な反復可能クラスを提供するのは困難でした(これについては別のコメントで説明しています)。

Guidoの決定は 履歴メール に記録され、str.join(seq)を決定します。

おかしいです、しかしそれは正しいようです!バリー、それに行きなさい...
- グイド・ヴァン・ロッサム

258

join()メソッドはリストクラスではなく文字列クラスに入っているのですか?

面白そうだね.

http://www.faqs.org/docs/diveintopython/odbchelper_join.htmlを参照してください

過去のメモ 初めてPythonを学んだとき、joinはリストのメソッドであることを期待していました。リストのメソッドは引数としてデリミタを取ります。多くの人が同じように感じており、joinメソッドの背後にはストーリーがあります。 Python 1.6より前では、文字列はこれらの便利なメソッドをすべて持っていませんでした。すべての文字列関数を含む別の文字列モジュールがありました。各関数は最初の引数として文字列を取りました。これらの関数は、文字列自体に配置するのに十分なほど重要であると見なされていました。これは、lower、upper、splitなどの関数にとって意味がありました。しかし、多くの中核的なPythonプログラマーは新しいjoinメソッドに反対し、それは代わりにリストのメソッドであるべきか、あるいはまったく動くべきではなく単に古いstringモジュールの一部のままにすべきだと主張しましたその中に便利なものの)。私は排他的に新しいjoinメソッドを使っていますが、どちらの方法で書かれたコードも見ることができますし、それが本当に気になる場合は、代わりに古いstring.join関数を使うことができます。

--- Mark Pilgrim、Pythonに飛び込む

242
Bill Karwin

最初は直感に反すると思いますが、それには正当な理由があります。 Joinはリストのメソッドにはなれません。

  • それは異なるイテラブル(タプル、ジェネレータなど)に対しても動作しなければなりません。
  • 異なる種類の文字列間では異なる動作をする必要があります。

実際には2つのjoinメソッドがあります(Python 3.0):

>>> b"".join
<built-in method join of bytes object at 0x00A46800>
>>> "".join
<built-in method join of str object at 0x00A28D40>

Joinがリストのメソッドであれば、その引数を調べてどれを呼び出すかを決める必要があります。そして、あなたはbyteとstrを一緒に結合することはできません。

62
Kiv

なぜstring.join(list)ではなくlist.join(string)なのですか?

これはjoinが "文字列"メソッドだからです!それは任意のiterableから文字列を作成します。リストにメソッドを貼り付けた場合、リストではないイテラブルがあるとどうなりますか?

あなたが文字列のタプルを持っているとどうなりますか?これがlistメソッドの場合、要素を単一の文字列に結合する前に、そのような文字列のすべてのイテレータをlistとしてキャストする必要があります。例えば:

some_strings = ('foo', 'bar', 'baz')

私たち自身のリストのjoinメソッドを転がしましょう:

class OurList(list): 
    def join(self, s):
        return s.join(self)

そしてそれを使用するには、まず最初に各イテラブルからリストを作成してそのイテラブルの文字列を結合しなければならず、メモリと処理能力の両方を無駄にしなければならないことに注意してください。

>>> l = OurList(some_strings) # step 1, create our list
>>> l.join(', ') # step 2, use our list join method!
'foo, bar, baz'

そのため、組み込みの文字列メソッドを使用するのではなく、listメソッドを使用するための追加の手順を追加する必要があることがわかります。

>>> ' | '.join(some_strings) # a single step!
'foo | bar | baz'

発電機に関する性能上の警告

Pythonがstr.joinで最終文字列を作成するために使用するアルゴリズムは、実際にはイテラブルを2回渡す必要があるため、ジェネレータ式を指定する場合は、最終文字列を作成する前にリストに具体化する必要があります。

したがって、ジェネレータを迂回する方が通常リスト内包表記よりも優れていますが、str.joinは例外です。

>>> import timeit
>>> min(timeit.repeat(lambda: ''.join(str(i) for i in range(10) if i)))
3.839168446022086
>>> min(timeit.repeat(lambda: ''.join([str(i) for i in range(10) if i])))
3.339879313018173

それにもかかわらず、str.join操作はまだ意味的に "文字列"操作である、それでそれは雑多なイテラブルよりstrオブジェクトの上にそれを持つことはまだ理にかなっています。

41
Aaron Hall

それを分割するための自然な直交操作と考えてください。

私はなぜそれがイテレータブルなものにも適用可能で、簡単に実装できないのか ちょうど リスト上で - 理解しています。

読みやすさのために、私は言語でそれを見たいのですが、私はそれが実際に実行可能であるとは思わない - もし反復可能性がインターフェースであればそれはインターフェースに追加されることができる反復可能なものの集合にそれを追加します。

23
Andy Dent

someString.join()の結果は文字列だからです。

シーケンス(リスト、タプル、その他)は結果には現れず、単なる文字列です。結果は文字列なので、文字列のメソッドとして意味があります。

12
S.Lott

" - "の-。join(my_list)は、リストの要素を結合することで文字列に変換することを宣言します。結果を重視しています。

参考のため、methods_of_stringの徹底的なチートシートを作成します。

string_methonds_44 = {
    'convert': ['join','split', 'rsplit','splitlines', 'partition', 'rpartition'],
    'edit': ['replace', 'lstrip', 'rstrip', 'strip'],
    'search': ['endswith', 'startswith', 'count', 'index', 'find','rindex', 'rfind',],
    'condition': ['isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isnumeric','isidentifier',
                  'islower','istitle', 'isupper','isprintable', 'isspace', ],
    'text': ['lower', 'upper', 'capitalize', 'title', 'swapcase',
             'center', 'ljust', 'rjust', 'zfill', 'expandtabs','casefold'],
    'encode': ['translate', 'maketrans', 'encode'],
    'format': ['format', 'format_map']}
6
JawSaw

どちらもいいじゃない。

string.join(xs、delimit)は、文字列モジュールは文字列に対してのみ機能するため、文字列モジュールがリストの存在を認識していることを意味します。

list.join(delimit)は、文字列が基本的な型であることに慣れているので(そして言語的にはそうであるため)、もう少し優れています。しかし、これはjoinが動的にディスパッチされる必要があることを意味します。なぜなら、a.split("\n")の任意のコンテキストではpythonコンパイラはaが何であるかを知らず、それを調べる必要があるからです。何度も。

リストが組み込みモジュールであることをPythonランタイムコンパイラが知っていれば、動的ルックアップをスキップしてその意図を直接バイトコードにエンコードすることができます。それ以外の場合、動的に "a"の "join"を解決する必要があります。呼び出しごとの継承権(pythonは動的言語であるため、呼び出し間で結合の意味が変わる可能性があります)。

残念ながら、これは抽象化の究極の欠陥です。どの抽象化を選択しても、その抽象化は解決しようとしている問題の文脈でしか意味がありません。したがって、それらを接着し始めても根本的なイデオロギーと矛盾しない一貫した抽象化はできません。あなたのイデオロギーと一致する見方でそれらを包むことなく一緒に。これを知って、pythonのアプローチはより安いのでより柔軟です、あなた自身のラッパーまたはあなた自身のプリプロセッサを作ることによって、それを "よりきれいに"見せるためにもっと支払うのはあなた次第です。

2
Dmitry