Dołączanie do listy:
>>> ''.join([ str(_) for _ in xrange(10) ])
'0123456789'
join
musi mieć iterowalny plik.
Najwyraźniej join
argumentem jest [ str(_) for _ in xrange(10) ]
i jest to zrozumienie listy .
Spójrz na to:
>>>''.join( str(_) for _ in xrange(10) )
'0123456789'
Otóż join
argumentem jest po prostu str(_) for _ in xrange(10)
nie []
, ale wynik jest taki sam.
Czemu? Czy str(_) for _ in xrange(10)
tworzy również listę lub iterowalną listę?
python
list-comprehension
Alcott
źródło
źródło
join
najprawdopodobniej jest napisane w C i dlatego działa znacznie szybciej niż zrozumienie listy ... Czas testu!_
nie ma specjalnego znaczenia, jest to zwykła nazwa zmiennej. Często jest używana jako nazwa do wyrzucenia, ale tak nie jest (używasz zmiennej). Unikałbym używania go w kodzie (przynajmniej w ten sposób).Odpowiedzi:
>>>''.join( str(_) for _ in xrange(10) )
Nazywa się to wyrażeniem generatora i zostało wyjaśnione w PEP 289 .
Główna różnica między wyrażeniami generatora a wyrażeniami list polega na tym, że te pierwsze nie tworzą listy w pamięci.
Zwróć uwagę, że istnieje trzeci sposób zapisania wyrażenia:
''.join(map(str, xrange(10)))
źródło
( str(_) for _ in xrange(10) )
. Ale byłem zdezorientowany, dlaczego()
można pominąć wjoin
, co oznacza, że kod powinien wyglądać jak `` '' .join ((str (_) for _ in xrange (10))), prawda?tup = 1, 2, 3; print(tup)
. Mając to na uwadze, użyciefor
jako części wyrażenia tworzy generator, a nawiasy służą tylko do odróżnienia go od nieprawidłowo napisanej pętli.Pozostali respondenci mieli rację, odpowiadając, że znalazłeś wyrażenie generatora (które ma notację podobną do wyrażeń listowych, ale bez otaczających nawiasów kwadratowych).
Ogólnie rzecz biorąc, geneksy (jak są czule znane) są bardziej wydajne w pamięci i szybsze niż rozumienia list.
JEDNAK w przypadku
''.join()
, rozumienie listy jest zarówno szybsze, jak i bardziej wydajne w pamięci. Powodem jest to, że złączenie musi wykonać dwa przejścia przez dane, więc w rzeczywistości potrzebuje prawdziwej listy. Jeśli go podasz, może natychmiast rozpocząć pracę. Jeśli zamiast tego podasz mu genexp, nie będzie mógł rozpocząć pracy, dopóki nie utworzy nowej listy w pamięci, uruchamiając genexp do wyczerpania:~ $ python -m timeit '"".join(str(n) for n in xrange(1000))' 1000 loops, best of 3: 335 usec per loop ~ $ python -m timeit '"".join([str(n) for n in xrange(1000)])' 1000 loops, best of 3: 288 usec per loop
Ten sam wynik zachodzi przy porównaniu itertools.imap z mapą :
~ $ python -m timeit -s'from itertools import imap' '"".join(imap(str, xrange(1000)))' 1000 loops, best of 3: 220 usec per loop ~ $ python -m timeit '"".join(map(str, xrange(1000)))' 1000 loops, best of 3: 212 usec per loop
źródło
''.join()
do zbudowania łańcucha potrzebne są 2 przejścia przez iterator?Twój drugi przykład używa raczej wyrażenia generatora niż rozumienia listy. Różnica polega na tym, że przy zrozumieniu list lista jest całkowicie budowana i przekazywana do
.join()
. Dzięki wyrażeniu generatora elementy są generowane jeden po drugim i używane przez.join()
. Ten ostatni zużywa mniej pamięci i jest generalnie szybszy.Tak się składa, że konstruktor listy z radością wykorzysta każdą iterację, w tym wyrażenie generatora. Więc:
[str(n) for n in xrange(10)]
to tylko „cukier syntaktyczny” dla:
list(str(n) for n in xrange(10))
Innymi słowy, rozumienie listy jest po prostu wyrażeniem generatora, które jest przekształcane w listę.
źródło
[str(x) for x in xrange(1000)]
262 usec,:list(str(x) for x in xrange(1000))
304 usec.seq = PySequence_Fast(orig, "");
i jest to jedyny powód, dla którego iteratory działają wolniej niż listy lub krotki podczas wywoływania str.join (). Zapraszamy do rozpoczęcia czatu, jeśli chcesz o nim dalej dyskutować (jestem autorem PEP 289, twórcą kodu operacji LIST_APPEND i tym, który zoptymalizował konstruktor list (), więc mam trochę znajomość zagadnienia).Jak wspomniano, jest to wyrażenie generatora .
Z dokumentacji:
źródło
Jeśli jest w parens, ale nie w nawiasach, jest technicznie wyrażeniem generatora. Wyrażenia generatora zostały po raz pierwszy wprowadzone w Pythonie 2.4.
http://wiki.python.org/moin/Generators
Część po złączeniu
( str(_) for _ in xrange(10) )
jest sama w sobie wyrażeniem generującym. Możesz zrobić coś takiego:mylist = (str(_) for _ in xrange(10)) ''.join(mylist)
i oznacza to dokładnie to samo, co napisałeś w drugim przypadku powyżej.
Generatory mają kilka bardzo interesujących właściwości, z których najważniejszą jest to, że nie przydzielają całej listy, gdy jej nie potrzebujesz. Zamiast tego funkcja taka jak join „wypompowuje” elementy z wyrażenia generatora pojedynczo, wykonując swoją pracę na malutkich częściach pośrednich.
W twoich konkretnych przykładach lista i generator prawdopodobnie nie działają strasznie inaczej, ale generalnie wolę używać wyrażeń generatora (a nawet funkcji generatora), kiedy tylko mogę, głównie dlatego, że niezwykle rzadko generator działa wolniej niż pełna lista materializacja.
źródło
To jest raczej generator niż rozumienie listy. Generatory są również iterowalne, ale zamiast najpierw tworzyć całą listę, a następnie przekazywać ją do złączenia, przekazuje każdą wartość z zakresu x po kolei, co może być znacznie bardziej wydajne.
źródło
Argumentem twojego drugiego
join
wywołania jest wyrażenie generatora. Tworzy iterowalny plik.źródło