it-swarm.com.de

Python join: Warum ist es string.join (Liste) anstelle von list.join (Zeichenfolge)?

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 dafür?

1622
Evan Fosmark

Es liegt daran, dass jedes iterative Element verbunden werden kann, nicht nur Listen, sondern das Ergebnis und der "Joiner" sind immer Zeichenfolgen.

Z.B:

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

Dies wurde im Thread String methods ... finally im Python-Dev-Archiv besprochen und 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ützte). Python 2.0 (unterstützte 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 integrierte Funktion
  • Guido wollte nicht nur lists, Tuples, sondern alle Sequenzen/Iterables unterstützen.
  • seq.reduce(str) ist für Neulinge schwierig.
  • seq.join(str) führt eine unerwartete Abhängigkeit von Sequenzen zu str/unicode ein.
  • join() als integrierte Funktion würde nur bestimmte Datentypen unterstützen. Ein eingebauter Namespace ist also nicht gut. Wenn join() viele Datentypen unterstützt, ist die Erstellung einer optimierten Implementierung schwierig. Wenn die Implementierung mit der __add__ -Methode erfolgt, ist sie O (n²).
  • Die Trennzeichenfolge (sep) sollte nicht weggelassen werden. Explizit ist besser als implizit.

In diesem Thread werden keine weiteren Gründe aufgeführt.

Hier sind einige zusätzliche Gedanken (meine eigenen und die meines Freundes):

  • Die Unicode-Unterstützung kam, war aber nicht endgültig. Zu diesem Zeitpunkt war UTF-8 der wahrscheinlichste Ersatz für UCS2/4. Um die Gesamtpufferlänge von UTF-8-Zeichenfolgen zu berechnen, muss die Zeichencodierungsregel bekannt sein.
  • Zu diesem Zeitpunkt hatte Python bereits eine gemeinsame Sequenzschnittstellenregel festgelegt, in der ein Benutzer eine sequenzähnliche (iterierbare) Klasse erstellen konnte. Aber Python hat das Erweitern von eingebauten Typen bis 2.2 nicht unterstützt. Zu dieser Zeit war es schwierig, grundlegende iterable Klasse (die in einem anderen Kommentar erwähnt wird) bereitzustellen.

Guidos Entscheidung wird in einer historischen Mail aufgezeichnet, die über str.join(seq) entscheidet:

Komisch, aber es scheint richtig! Barry, mach schon ...
- Guido van Rossum

275

Befindet sich die Methode join() in der Zeichenfolgenklasse anstelle der Listenklasse?

Ich bin damit einverstanden, dass es lustig aussieht.

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

Historischer Hinweis. Als ich Python zum ersten Mal lernte, erwartete ich, dass join eine Methode einer Liste ist, bei der das Trennzeichen als Argument verwendet wird. Vielen Menschen geht es genauso, und hinter der Join-Methode verbirgt sich eine Geschichte. Vor Python 1.6 hatten Strings nicht alle diese nützlichen Methoden. Es gab ein separates Zeichenkettenmodul, das alle Zeichenkettenfunktionen enthielt. Für jede Funktion wurde als erstes Argument eine Zeichenfolge verwendet. Die Funktionen wurden als wichtig genug erachtet, um sie auf die Zeichenfolgen selbst zu übertragen, was für Funktionen wie Lower, Upper und Split Sinn machte. Aber viele Hardcore-ProgrammiererPythonhaben Einwände gegen die neue Join-Methode erhoben und argumentiert, dass sie stattdessen eine Methode der Liste sein sollte oder dass sie sich überhaupt nicht bewegen sollte, sondern einfach ein Teil der alten Zeichenfolge bleiben sollte Modul (das noch viele nützliche Dinge enthält). Ich verwende ausschließlich die neue Join-Methode, aber Sie werden sehen, dass Code in beide Richtungen geschrieben wurde. Wenn Sie das wirklich stört, können Sie stattdessen die alte Funktion string.join verwenden.

--- Mark Pilgrim, tauche ein in Python

245
Bill Karwin

Ich stimme zu, dass es zunächst nicht intuitiv 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 Arten von Zeichenfolgen 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 sie ihre Argumente prüfen, um zu entscheiden, welche von ihnen aufgerufen werden soll. Und Sie können nicht Byte und Str zusammenfügen, daher macht die Art und Weise, wie sie es jetzt haben, Sinn.

63
Kiv

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

Dies liegt daran, dass join eine "String" -Methode ist! Es wird eine Zeichenfolge aus einer beliebigen iterablen Datei erstellt. Wenn wir die Methode auf Listen festhalten, wie sieht es dann aus, wenn es iterables gibt, die keine Listen sind?

Was ist, wenn Sie ein Tupel von Zeichenfolgen haben? Wenn dies eine list Methode wäre, müssten Sie jeden solchen Iterator von Zeichenketten als list umwandeln, bevor Sie die Elemente zu einer einzigen Zeichenkette zusammenfügen könnten! Zum Beispiel:

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

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

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

Beachten Sie, dass wir zunächst aus jedem Iterationsschritt eine Liste erstellen müssen, um die Zeichenfolgen in diesem Iterationsschritt zu verknüpfen, wodurch sowohl Speicher als auch Rechenleistung verschwendet werden:

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

Wir müssen also einen zusätzlichen Schritt hinzufügen, um unsere Listenmethode zu verwenden, anstatt nur die eingebaute Zeichenfolgenmethode zu verwenden:

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

Leistungshindernis für Generatoren

Der Algorithmus Python, der zum Erstellen des endgültigen Strings mit str.join verwendet wird, muss das Iterable tatsächlich zweimal durchlaufen. Wenn Sie ihm also einen Generatorausdruck bereitstellen, muss er es zuerst in einer Liste materialisieren kann die endgültige Zeichenfolge erstellen.

Während das Weitergeben von Generatoren normalerweise besser ist als das Verstehen von Listen, ist str.join 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

Trotzdem ist die str.join -Operation semantisch immer noch eine "String" -Operation, daher ist es immer noch sinnvoll, sie für das str -Objekt zu haben als für verschiedene Iterables.

42
Aaron Hall

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

Ich verstehe, warum es auf alles Iterable anwendbar ist und so nicht einfach implementiert werden kann nur auf Liste.

Aus Gründen der Lesbarkeit würde ich es gerne in der Sprache sehen, aber ich denke nicht, dass dies tatsächlich machbar ist. Wenn es sich bei der Iterabilität um eine Schnittstelle handeln würde, könnte sie der Schnittstelle hinzugefügt werden, dies ist jedoch nur eine Konvention, und es gibt keinen zentralen Weg dazu füge es der Menge der Dinge hinzu, die iterabel sind.

23
Andy Dent

In erster Linie, weil das Ergebnis von someString.join() eine Zeichenfolge ist.

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

12
S.Lott

- in "-". Join (my_list) gibt an, dass Sie eine Liste aus dem Verbinden von Elementen in eine Zeichenfolge konvertieren. Dies ist ergebnisorientiert (nur zum leichteren Speichern und Verstehen).

Ich erstelle ein ausführliches Cheatsheet von Methods_of_string als Referenz.

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']}
7
Algebra

Beides ist nicht schön.

string.join (xs, delimit) bedeutet, dass das Zeichenfolgenmodul das Vorhandensein einer Liste kennt, von der es nichts weiß, da das Zeichenfolgenmodul nur mit Zeichenfolgen arbeitet.

list.join (delimit) ist ein bisschen netter, weil wir es so gewohnt sind, dass Strings ein grundlegender Typ sind (und das sind sie auch in der Sprache). Dies bedeutet jedoch, dass der Join dynamisch ausgelöst werden muss, da im beliebigen Kontext von a.split("\n") der python -Compiler möglicherweise nicht weiß, was ein ist, und nachschlagen muss (analog zu vtable lookup). Das ist teuer, wenn Sie es oft tun.

wenn der python -Runtime-Compiler weiß, dass list ein integriertes Modul ist, kann er die dynamische Suche überspringen und die Absicht direkt in den Bytecode codieren, während er andernfalls "join" von "a" dynamisch auflösen muss. Dies kann mehrere Vererbungsebenen pro Aufruf umfassen (da sich die Bedeutung von Join zwischen den Aufrufen möglicherweise geändert hat, da python eine dynamische Sprache ist).

leider ist dies der ultimative Fehler der Abstraktion; Unabhängig davon, für welche Abstraktion Sie sich entscheiden, ist Ihre Abstraktion nur im Kontext des Problems sinnvoll, das Sie lösen möchten, und als solches können Sie niemals eine konsistente Abstraktion erhalten, die nicht mit den zugrunde liegenden Ideologien unvereinbar ist, wenn Sie anfangen, sie zusammenzufügen zusammen, ohne sie in eine Ansicht zu hüllen, die mit Ihrer Ideologie vereinbar ist. Wenn Sie dies wissen, ist der Ansatz von Python flexibler, da er billiger ist. Es liegt an Ihnen, mehr zu bezahlen, damit er "schöner" aussieht, entweder indem Sie Ihren eigenen Wrapper oder Ihren eigenen Präprozessor erstellen.

2
Dmitry