with - python join list separator




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

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é è 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'))

Pensala come l'operazione ortogonale naturale da dividere.

Capisco perché sia ​​applicabile a qualsiasi cosa iterabile e quindi non può essere facilmente implementata solo sulla lista.

Per leggibilità, mi piacerebbe vederlo nella lingua, ma non penso che sia effettivamente fattibile - se l'iterabilità fosse un'interfaccia allora potrebbe essere aggiunta all'interfaccia ma è solo una convenzione e quindi non c'è un modo centrale per aggiungilo all'insieme di cose che sono iterabili.


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


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.





join