Frage Python-Join: Warum ist es string.join (list) statt list.join (string)?


Das hat mich immer verwirrt. Es scheint, als wäre das schöner:

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

Als das:

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

Gibt es einen bestimmten Grund, warum es so ist?


1378
2018-01-29 22:45


Ursprung


Antworten:


Weil jedes iterable verknüpft werden kann, nicht nur Listen, sondern das Ergebnis und der "Joiner" sind immer Strings.

Z.B:

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

998
2018-01-29 22:51



Weil das join() Methode ist in der String-Klasse statt der List-Klasse?

Ich stimme zu, dass es lustig aussieht.

Sehen http://www.faqs.org/docs/diveintopython/odbchelper_join.html:

Historische Anmerkung. Als ich das erste Mal gelernt habe   Python, ich habe erwartet, dass Join eine Methode ist   einer Liste, die die nehmen würde   Trennzeichen als Argument. Viele   Menschen fühlen sich genauso, und da ist es   eine Geschichte hinter der Join-Methode. Vorher   In Python 1.6 hatten Strings nicht alle   diese nützlichen Methoden. Da war ein   separates String-Modul, das enthielt   alle String-Funktionen; jeder   Funktion nahm eine Zeichenfolge als erste   Streit. Die Funktionen wurden berücksichtigt   wichtig genug, um auf die   Strings selbst, was einen Sinn ergab   für Funktionen wie unten, oben und   Teilt. Aber viele Hardcore-Python   Programmierer protestierten gegen den neuen Beitritt   Methode, argumentierend, dass es a sein sollte   Methode der Liste statt, oder dass es   sollte sich überhaupt nicht bewegen, sondern einfach bleiben   ein Teil des alten String - Moduls (welches   hat immer noch viele nützliche Sachen drin).   Ich benutze ausschließlich die neue Join-Methode,   aber Sie werden auch Code geschrieben sehen   und wenn es dich wirklich stört   kann die alte string.join-Funktion verwenden   stattdessen.

--- Mark Pilgrim, tauchen Sie in Python ein


227
2018-01-29 22:48



Dies wurde in der diskutiert String-Methoden ... endlich Thread in der Python-Dev achive, und wurde von Guido akzeptiert. Dieser Thread begann im Juni 1999, und str.join wurde in Python 1.6 aufgenommen, das im September 2000 veröffentlicht wurde (und Unicode unterstützt). Python 2.0 (unterstützt str Methoden einschließlich join) wurde im Oktober 2000 veröffentlicht.

  • In diesem Thread wurden vier Optionen vorgeschlagen:
    • str.join(seq)
    • seq.join(str)
    • seq.reduce(str)
    • join als eine eingebaute Funktion
  • Guido wollte nicht nur unterstützen lists, tuples, aber alle Sequenzen / iterables.
  • seq.reduce(str) ist schwierig für Neulinge.
  • seq.join(str) führt eine unerwartete Abhängigkeit von Sequenzen zu str / unicode ein.
  • join() da eine integrierte Funktion nur bestimmte Datentypen unterstützt. Die Verwendung eines eingebauten Namespace ist nicht gut. Ob join() unterstützt viele Datentypen, die Erstellung einer optimierten Implementierung wäre schwierig, wenn sie unter Verwendung der __add__ Methode dann ist es O (n²).
  • Die Trennzeichenfolge (sep) sollte nicht weggelassen werden. Explizit ist besser als implizit.

In diesem Thread werden keine weiteren Gründe angeboten.

Hier sind einige zusätzliche Gedanken (meine eigene und die meiner Freundin):

  • Unicode-Unterstützung kam, aber es war nicht endgültig. Zu dieser Zeit war UTF-8 am ehesten in der Lage, UCS2 / 4 zu ersetzen. Um die Gesamtpufferlänge von UTF-8-Strings zu berechnen, muss die Zeichencodierungsregel bekannt sein.
  • Zu dieser Zeit hatte Python bereits eine gemeinsame Sequenzschnittstellenregel festgelegt, bei der ein Benutzer eine sequenzähnliche (iterierbare) Klasse erstellen konnte. Python unterstützte jedoch das Erweitern integrierter Typen erst ab Version 2.2. Zu dieser Zeit war es schwierig, eine iterierbare Basisklasse bereitzustellen (was in einem anderen Kommentar erwähnt wird).

Guidos Entscheidung ist in einem aufgezeichnet historische Postsich entscheiden str.join(seq):

Lustig, aber es scheint richtig! Barry, geh drauf ...
  --Guido van Rossum


211
2017-09-30 15:21



Ich stimme zu, dass es zunächst nicht einleuchtend ist, aber es gibt einen guten Grund. Join kann keine Methode einer Liste sein, weil:

  • es muss auch für verschiedene Iterables funktionieren (Tupel, Generatoren, etc.)
  • Es muss ein unterschiedliches Verhalten zwischen verschiedenen Stringtypen aufweisen.

Es gibt zwei Join-Methoden (Python 3.0):

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

Wenn Join eine Methode einer Liste wäre, müsste es seine Argumente überprüfen, um zu entscheiden, welcher von ihnen aufgerufen werden soll. Und Sie können nicht Byte und Str zusammenfügen, also macht die Art, wie sie sie haben, jetzt Sinn.


58
2018-01-29 23:03



Warum ist es string.join(list) Anstatt von list.join(string)?

Das ist weil join ist eine "String" -Methode! Es erstellt eine Zeichenfolge aus jedem iterablen. Wenn wir die Methode auf Listen stecken, was ist, wenn wir Iterables haben, die keine Listen sind?

Was ist, wenn Sie ein Tupel von Strings haben? Wenn das ein war list Methode, müssten Sie jeden solchen Iterator von Strings als list bevor Sie die Elemente zu einer einzigen Zeichenfolge verbinden konnten! Beispielsweise:

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

Lassen Sie uns unsere eigene Listen-Join-Methode rollen:

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

Und um es zu verwenden, müssen wir zuerst eine Liste von jedem iterablen Objekt erstellen, um die Strings in diesem iterablen Array zu verbinden, was sowohl Speicher- als auch Verarbeitungsleistung verschwendet:

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

So sehen wir, dass wir einen zusätzlichen Schritt hinzufügen müssen, um unsere Listenmethode zu verwenden, anstatt nur die eingebaute String-Methode zu verwenden:

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

Leistungseinschränkung für Generatoren

Der Algorithmus, mit dem Python die letzte Zeichenfolge mit erstellt str.join Tatsächlich muss das iterable zweimal durchlaufen werden. Wenn Sie also einen Generatorausdruck angeben, muss dieser zuerst in einer Liste materialisiert werden, bevor er die letzte Zeichenfolge erstellen kann.

So ist es zwar besser, Generatoren zu umgehen, als Listen-Comprehensions, str.join ist eine Ausnahme:

>>> 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

Dennoch, die str.join Operation ist immer noch semantisch eine "String" -Operation, so dass es immer noch sinnvoll ist, sie auf der str Objekt als auf verschiedenen iterables.


36
2018-04-14 00:45



Betrachten Sie es als die natürliche orthogonale Operation zum Teilen.

Ich verstehe, warum es auf alles iterable anwendbar ist und deshalb nicht einfach implementiert werden kann gerade auf der Liste.

Aus Gründen der Lesbarkeit würde ich es gerne in der Sprache sehen, aber ich glaube nicht, dass dies tatsächlich machbar ist - wenn die Iterabilität eine Schnittstelle wäre, dann könnte sie der Schnittstelle hinzugefügt werden, aber es ist nur eine Konvention und es gibt keinen zentralen Weg dazu füge es der Menge der Dinge hinzu, die iterierbar sind.


22
2018-01-30 02:43



Hauptsächlich weil das Ergebnis von a someString.join() ist eine Zeichenfolge.

Die Sequenz (Liste oder Tupel oder was auch immer) erscheint nicht im Ergebnis, nur eine Zeichenkette. Da das Ergebnis eine Zeichenfolge ist, ist es als eine Methode einer Zeichenfolge sinnvoll.


11
2018-01-29 22:51



Beide sind nicht nett.

string.join (xs, delimit) bedeutet, dass dem String-Modul die Existenz einer Liste bekannt ist, über die es nichts wissen muss, da das String-Modul nur mit Strings arbeitet.

list.join (delimit) ist ein bisschen netter, weil wir uns daran gewöhnt haben, dass Strings ein grundlegender Typ sind (und lingual gesprochen sind sie es auch). Dies bedeutet jedoch, dass Join dynamisch ausgelöst werden muss, da im beliebigen Kontext von a.split("\n") Der Python-Compiler weiß möglicherweise nicht, was ein is ist, und muss es nachschlagen (analog zu vtable lookup), was teuer ist, wenn Sie es oft tun.

Wenn der Python-Laufzeitcompiler weiß, dass die Liste ein eingebautes Modul ist, kann er die dynamische Suche überspringen und die Absicht direkt in den Bytecode codieren, während er andernfalls den Join von "a", der mehrere Ebenen umfassen kann, dynamisch auflösen muss der Vererbung pro Aufruf (da sich zwischen den Aufrufen die Bedeutung von Join möglicherweise geändert hat, weil Python eine dynamische Sprache ist).

Leider ist dies der ultimative Fehler der Abstraktion; Ganz gleich, für welche Abstraktion Sie sich entscheiden, Ihre Abstraktion wird nur im Zusammenhang mit dem Problem, das Sie zu lösen versuchen, sinnvoll sein, und Sie können niemals eine konsistente Abstraktion haben, die nicht inkonsistent mit zugrundeliegenden Ideologien wird, wenn Sie anfangen, sie zu kleben zusammen, ohne sie in eine Sichtweise zu verpacken, die Ihrer Ideologie entspricht. Mit diesem Wissen ist Pythons Ansatz flexibler, da es billiger ist. Es liegt an Ihnen, mehr dafür zu zahlen, damit es "netter" aussieht, entweder indem Sie Ihren eigenen Wrapper oder Ihren eigenen Präprozessor erstellen.


1
2018-05-07 19:32