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


1762

これはいつも私を混乱させました。これはより良いようです:

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

これより:

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

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


1
記憶と理解を容易に-するために、リストに参加して文字列に変換することを宣言します。結果指向です。
微積分

11
@JawSaw:memを混乱させるだけです。
einpoklum

34
短い答えは、Pythonの型システムは十分に強力ではないstrためであり、反復可能なすべての型に実装するよりも、一度この機能を実装する方が簡単だったからだと思います。
BallpointBen 2018年

3
join()は文字列を返すため、文字列コンテキストから呼び出さなければならないというのが元々の考えです。リストにjoin()を置いても、リストはオブジェクトのコンテナであり、文字列のみに固有の1回限りの関数を持つべきではないので、それほど意味がありません。
ジョシュアバーンズ

回答:


1248

これは、任意のイテラブル(たとえば、リスト、タプル、dict、セット)を結合できるためですが、結果と「結合子」文字列でなければなりません

例えば:

'_'.join(['welcome', 'to', 'stack', 'overflow'])
'_'.join(('welcome', 'to', 'stack', 'overflow'))
'welcome_to_stack_overflow'

文字列以外のものを使用すると、次のエラーが発生します。

TypeError:シーケンス項目0:予期されるstrインスタンス、intが見つかりました


57
コード的に理にかなっているとしても、概念的には同意しません。list.join(string)よりオブジェクト指向のアプローチのように見えますが、string.join(list)私にははるかに手続き型に聞こえます。
Eduardo Pignatelli、2018年

22
では、なぜiterableに実装されていないのでしょうか?
SteenSchütt2018年

10
@TimeSheep:反復可能であっても、整数のリストには意味のある結合がありません。
再帰的

16
私は使用してみましたがprint(str.join('-', my_list))、うまくいきました。
pimgeek

13
@TimeSheep iterableは具象型ではないため、iterableはインターフェースであり、__iter__メソッドを定義する任意の型です。すべての反復可能オブジェクトも実装する必要があるjoinと、非常に特殊なユースケースでは、一般的なインターフェース(文字列以外の反復可能オブジェクトもカバーする)が複雑になります。joinstrinsで定義すると、「直感的でない」注文を犠牲にしてこの問題を回避できます。最初の引数がイテラブルで2番目の引数(オプション)がジョイナー文字列である関数を維持することをお勧めしますが、その船は航行しました。
user4815162342 2018年

319

これは文字列メソッドで議論されました...最後にPython-Devアーカイブでスレッド化され、Guidoに受け入れられました。このスレッドは1999年6月に始まり、str.join2000年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までサポートしていませんでした。当時、基本的な反復可能なクラスを提供することは困難でした(別のコメントで言及されています)。

グイドの決定は歴史的なメールに記録され、次のことを決定しstr.join(seq)ます:

おかしいが、それは正しいようです!バリー、
頑張って... --Guido van Rossum


251

のでjoin()この方法ではなく、リストクラスで、Stringクラスにありますか?

面白そうですね。

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

歴史ノート。私が最初にPythonを学んだとき、私はjoinがリストのメソッドであることを期待していました。これは、区切り文字を引数として受け取ります。多くの人が同じように感じており、joinメソッドの背後にはストーリーがあります。Python 1.6以前では、文字列にはこれらの便利なメソッドがすべてありませんでした。すべての文字列関数を含む個別の文字列モジュールがありました。各関数は最初の引数として文字列を受け取りました。関数は、文字列自体に配置するのに十分重要であると見なされました。これは、lower、upper、splitなどの関数には意味があります。しかし、多くのハードコアPythonプログラマーは、新しいjoinメソッドに反対し、代わりにリストのメソッドである必要がある、または単に移動するのではなく、古い文字列モジュールの一部のままである必要があると主張しました(まだたくさんあります)その中の有用なものの)。

---マークピルグリム、Dive into Python


12
Python 3 stringライブラリは冗長なstrメソッドをすべて削除したため、を使用できなくなりましたstring.join()。個人的には、それが「おかしい」とは思っていませんでした。リストだけでなく他のものにも参加できるので、それは完全に理にかなっていますが、ジョイナーは常に文字列です!
Martijn Pieters

67

最初は直感に反することには同意しますが、それには正当な理由があります。次の理由により、Joinをリストのメソッドにすることはできません。

  • さまざまなイテラブル(タプル、ジェネレータなど)でも機能する必要があります。
  • 文字列の種類によって動作が異なる必要があります。

実際には2つの結合方法があります(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を一緒に結合することはできないので、それらの方法は理にかなっています。


45

なぜstring.join(list)代わりにlist.join(string)

これはjoin「文字列」メソッドだからです!イテラブルから文字列を作成します。リストにメソッドを貼り付けた場合、リストではない反復可能オブジェクトがある場合はどうでしょうか?

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

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

独自のリスト結合メソッドをロールしてみましょう:

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、その他の反復可能オブジェクトよりもオブジェクト上で操作する方が理にかなっています。


24

分割する自然な直交演算と考えてください。

なぜそれが反復可能なものに適用できるのか理解しているので、リストだけでは簡単に実装することはできません。

読みやすさのために、私はそれを言語で見たいのですが、実際には実現可能ではないと思います-反復可能性がインターフェースであった場合、それをインターフェースに追加することができますが、それは単なる慣習であり、そのために中心的な方法はありませんそれを反復可能なもののセットに追加します。


13

主な理由は、aの結果がsomeString.join()文字列だからです。

シーケンス(リストまたはタプルなど)は結果に表示されず、文字列のみが表示されます。結果は文字列なので、文字列のメソッドとして意味があります。


10

- "-"。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']}

3

どちらも良くない。

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

list.join(delimit)は、文字列が基本的な型であることに慣れているため(言語的に言えば、そうです)、もう少しいいです。ただし、これはa.split("\n")、Pythonコンパイラの任意のコンテキストではaが何であるかを認識できず、それをルックアップする必要があるため(vtableルックアップと同様に)、結合を動的にディスパッチする必要があることを意味します。回。

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

悲しいことに、これは抽象化の究極の欠点です。どの抽象化を選択しても、抽象化は、解決しようとしている問題のコンテキストでのみ意味を持ちます。したがって、根底にあるイデオロギーとの接着を開始するときに一貫性のない抽象化を維持することはできません。あなたのイデオロギーと一致するビューでそれらを包むことなく一緒に。これを知って、Pythonのアプローチはより安価であるためより柔軟です。独自のラッパーまたは独自のプリプロセッサを作成することにより、「見栄え」をよくするために多くを支払うのはあなた次第です。


0

変数my_list"-"はどちらもオブジェクトです。具体的には、これらはそれぞれクラスlistとのインスタンスですstrjoinこの関数はクラスに属しますstr。したがって、"-".join(my_list)オブジェクト"-"my_list入力として使用されるため、構文が使用されます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.