concatenate - Python join:perché è string.join(lista)invece di list.join(stringa)?




of strings (9)

Perché è string.join(list) invece di list.join(string) ?

Questo perché join è un metodo "stringa"! Crea una stringa da qualsiasi iterabile. Se abbiamo bloccato il metodo sugli elenchi, e quando abbiamo iterable che non sono elenchi?

Cosa succede se hai una tupla di archi? Se questo fosse un metodo di list , dovresti lanciare ogni tale iteratore di stringhe come una list prima di poter unire gli elementi in una singola stringa! Per esempio:

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

Facciamo rotolare il nostro metodo di join della lista:

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

E per usarlo, nota che dobbiamo prima creare una lista da ogni iterabile per unire le stringhe in quella iterabile, sprecando sia la memoria che la potenza di elaborazione:

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

Quindi vediamo che dobbiamo aggiungere un ulteriore passaggio per usare il nostro metodo list, invece di usare solo il metodo della stringa incorporata:

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

Avvertenza per le prestazioni dei generatori

L'algoritmo usato da Python per creare la stringa finale con str.join realtà deve passare sopra l'iterabile due volte, quindi se si fornisce un'espressione di generatore, deve materializzarlo in un elenco prima di poter creare la stringa finale.

Quindi, mentre passare attorno ai generatori è solitamente meglio delle list comprehensions, str.join è un'eccezione:

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

Tuttavia, l'operazione str.join è ancora semanticamente un'operazione "stringa", quindi ha ancora senso averla sull'oggetto str che su iterables vari.

Questo mi ha sempre confuso. Sembra che questo sarebbe più bello:

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

Di questo:

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

C'è una ragione specifica è così?


Perché il metodo join() trova nella classe string, anziché nella classe list?

Sono d'accordo che sembra divertente.

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

Nota storica Quando ho imparato Python per la prima volta, mi aspettavo che join fosse un metodo di una lista, che avrebbe preso il delimitatore come argomento. Molte persone si sentono allo stesso modo, e c'è una storia dietro il metodo di join. Prima di Python 1.6, le stringhe non avevano tutti questi metodi utili. C'era un modulo di stringa separato che conteneva tutte le funzioni di stringa; ogni funzione ha preso una stringa come primo argomento. Le funzioni erano considerate abbastanza importanti da essere inserite nelle stringhe stesse, il che aveva senso per funzioni come lower, upper e split. Ma molti programmatori Python hard-core hanno contestato il nuovo metodo di join, sostenendo che dovrebbe invece essere un metodo della lista, o che non dovrebbe muoversi affatto, ma semplicemente mantenere una parte del vecchio modulo di stringa (che ha ancora molti di cose utili in esso). Io uso esclusivamente il nuovo metodo di join, ma vedrai codice scritto in entrambi i modi, e se ti infastidisce, puoi usare la vecchia funzione string.join.

--- Mark Pilgrim, Immergiti in Python


Questo è stato discusso nei metodi String ... finalmente thread nel Python-Dev achive, ed è stato accettato da Guido. Questa discussione è iniziata nel giugno 1999 e str.join stato incluso in Python 1.6 che è stato rilasciato nel settembre 2000 (e supportato Unicode). Python 2.0 (metodi str supportati incluso join ) è stato rilasciato nell'ottobre 2000.

  • C'erano quattro opzioni proposte in questo thread:
    • str.join(seq)
    • seq.join(str)
    • seq.reduce(str)
    • join come funzione integrata
  • Guido voleva supportare non solo le list s, le tuple , ma tutte le sequenze / iterabili.
  • seq.reduce(str) è difficile per i neofiti.
  • seq.join(str) introduce una dipendenza inattesa dalle sequenze a str / unicode.
  • join() come funzione built-in supporterebbe solo tipi di dati specifici. Quindi usare uno spazio dei nomi integrato non è buono. Se join() supporta molti tipi di dati, la creazione di un'implementazione ottimizzata sarebbe difficile, se implementata utilizzando il metodo __add__ , allora è O (n²).
  • La stringa separater ( sep ) non dovrebbe essere omessa. L'esplicito è meglio che implicito.

Non ci sono altri motivi offerti in questa discussione.

Ecco alcuni pensieri aggiuntivi (il mio e il mio amico):

  • Il supporto Unicode stava arrivando, ma non era definitivo. A quel tempo era UTF-8 il più probabile a sostituire UCS2 / 4. Per calcolare la lunghezza totale del buffer delle stringhe UTF-8 è necessario conoscere la regola di codifica dei caratteri.
  • A quel tempo, Python aveva già deciso una regola dell'interfaccia di sequenza comune in cui un utente poteva creare una classe (iterable) simile a una sequenza. Ma Python non supportava l'estensione dei tipi built-in fino al 2.2. A quel tempo era difficile fornire una classe iterabile di base (che è menzionata in un altro commento).

La decisione di Guido è registrata in una posta storica , decidendo su str.join(seq) :

Divertente, ma sembra giusto! Barry, cercalo ...
--Guido van Rossum


Entrambi non sono carini.

string.join (xs, delimit) significa che il modulo stringa è a conoscenza dell'esistenza di un elenco, di cui non ha alcuna conoscenza aziendale, dal momento che il modulo stringa funziona solo con le stringhe.

list.join (delimit) è un po 'più bello perché siamo così abituati a stringere un tipo fondamentale (e linguale, lo sono). Tuttavia questo significa che il join deve essere inviato dinamicamente perché nel contesto arbitrario di a.split("\n") il compilatore Python potrebbe non sapere che cosa è un, e dovrà cercarlo (analogamente alla ricerca vtable), che è costoso se lo fai un sacco di volte.

se il compilatore di runtime python sa che l'elenco è un modulo incorporato, può saltare la ricerca dinamica e codificare direttamente l'intento nel bytecode, mentre altrimenti è necessario risolvere dinamicamente "join" di "a", che può essere su più livelli di ereditarietà per chiamata (poiché tra le chiamate, il significato di join può essere cambiato, perché python è un linguaggio dinamico).

purtroppo, questo è il difetto ultimo dell'astrazione; non importa quale astrazione tu scelga, la tua astrazione avrà senso solo nel contesto del problema che stai cercando di risolvere, e come tale non puoi mai avere un'astrazione coerente che non diventi incoerente con le ideologie sottostanti mentre inizi ad incollarle insieme senza avvolgerli in una visione coerente con la tua ideologia. Sapendo questo, l'approccio di python è più flessibile poiché è più economico, spetta a te pagare di più per renderlo più "carino", sia creando il tuo wrapper, sia il tuo preprocessore.


Principalmente perché il risultato di someString.join() è una stringa.

La sequenza (lista o tupla o qualsiasi altra cosa) non appare nel risultato, solo una stringa. Poiché il risultato è una stringa, ha senso come metodo di una stringa.


- in "-". join (my_list) dichiara che stai convertendo in una stringa da unire elementi a un elenco. È orientato ai risultati. (solo per memoria e comprensione facili)

Faccio un esaustivo cheatsheet di methods_of_string come riferimento.

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

Sono d'accordo che all'inizio è controintuitivo, ma c'è una buona ragione. Unire non può essere un metodo di un elenco perché:

  • deve funzionare anche per diversi iterabili (tuple, generatori, ecc.)
  • deve avere un comportamento diverso tra diversi tipi di stringhe.

Esistono in realtà due metodi di 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>

Se join era un metodo di una lista, allora avrebbe dovuto esaminare i suoi argomenti per decidere quale di loro chiamare. E non puoi unire byte e str, quindi il modo in cui lo hanno ora ha senso.


È perché è possibile unire qualsiasi iterabile, non solo le liste, ma il risultato e il "joiner" sono sempre stringhe.

PER ESEMPIO:

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

Nota : qui sotto si applica a Python 3.3+ perché utilizza yield_from. sixè anche un pacchetto di terze parti, sebbene sia stabile. In alternativa, è possibile utilizzare sys.version.

Nel caso di obj = [[1, 2,], [3, 4], [5, 6]], tutte le soluzioni qui sono buone, compresa la comprensione delle liste e itertools.chain.from_iterable.

Tuttavia, considera questo caso leggermente più complesso:

>>> obj = [[1, 2, 3], [4, 5], 6, 'abc', [7], [8, [9, 10]]]

Ci sono diversi problemi qui:

  • Un elemento,, 6è solo uno scalare; non è iterable, quindi le rotte di cui sopra falliranno qui.
  • Un elemento, 'abc', è tecnicamente iterabile (tutti strs sono). Tuttavia, leggendo tra le righe un po ', non si vuole trattarlo come tale - si vuole trattarlo come un singolo elemento.
  • L'elemento finale, [8, [9, 10]]è esso stesso un iterabile nidificato. Comprensione di base delle liste e chain.from_iterablesolo estrarre "1 livello in basso".

Puoi rimediare a questo come segue:

>>> from collections import Iterable
>>> from six import string_types

>>> def flatten(obj):
...     for i in obj:
...         if isinstance(i, Iterable) and not isinstance(i, string_types):
...             yield from flatten(i)
...         else:
...             yield i


>>> list(flatten(obj))
[1, 2, 3, 4, 5, 6, 'abc', 7, 8, 9, 10]

Qui, si controlla che l'elemento secondario (1) sia iterabile con Iterable, un ABC da itertools, ma si desidera anche assicurarsi che (2) l'elemento non sia "simile a una stringa".





python string list join