Czasami muszę powtórzyć listę w Pythonie, patrząc na „bieżący” element i „następny” element. Do tej pory robiłem to z kodem takim:
for current, next in zip(the_list, the_list[1:]):
# Do something
To działa i robi to, czego oczekuję, ale czy istnieje bardziej idiomatyczny lub skuteczny sposób na zrobienie tego samego?
next
tego sposobu maskuje wbudowaną.next
jest również funkcją wbudowaną w Pythonie 2.Odpowiedzi:
Oto odpowiedni przykład z dokumentacji modułu itertools :
import itertools def pairwise(iterable): "s -> (s0,s1), (s1,s2), (s2, s3), ..." a, b = itertools.tee(iterable) next(b, None) return zip(a, b)
W przypadku Pythona 2
itertools.izip
zamiastzip
:import itertools def pairwise(iterable): "s -> (s0,s1), (s1,s2), (s2, s3), ..." a, b = itertools.tee(iterable) next(b, None) return itertools.izip(a, b)
Jak to działa:
Najpierw tworzone są dwa równoległe iteratory
a
ib
(tee()
wywołanie), oba wskazujące na pierwszy element oryginalnego iterowalnego. Drugi iteratorb
jest przesuwany o 1 krok do przodu (next(b, None)
wywołanie). W tym momenciea
wskazuje na s0 ib
wskazuje na s1. Obaa
ib
mogą niezależnie przechodzić przez oryginalny iterator - funkcja izip przyjmuje dwa iteratory i tworzy pary zwracanych elementów, przesuwając oba iteratory w tym samym tempie.Jedno zastrzeżenie:
tee()
funkcja generuje dwa iteratory, które mogą się rozwijać niezależnie od siebie, ale wiąże się to z pewnym kosztem. Jeśli jeden z iteratorów posuwa się dalej niż drugi, totee()
musi przechowywać zużyte elementy w pamięci, dopóki drugi iterator również ich nie pochłonie (nie może „przewinąć” oryginalnego iteratora). Tutaj nie ma to znaczenia, ponieważ jeden iterator jest tylko o krok przed drugim, ale ogólnie w ten sposób można łatwo wykorzystać dużo pamięci.A ponieważ
tee()
może przyjmowaćn
parametr, można go również użyć dla więcej niż dwóch równoległych iteratorów:def threes(iterator): "s -> (s0,s1,s2), (s1,s2,s3), (s2, s3,4), ..." a, b, c = itertools.tee(iterator, 3) next(b, None) next(c, None) next(c, None) return zip(a, b, c)
źródło
zip(ł, ł[1:])
jest znacznie krótszy ifuncy
module:funcy.pairwise
: funcy.readthedocs.io/en/stable/seqs.html#pairwiseSkręć swój własny!
def pairwise(iterable): it = iter(iterable) a = next(it, None) for b in it: yield (a, b) a = b
źródło
Ponieważ
the_list[1:]
faktycznie tworzy kopię całej listy (z wyłączeniem jej pierwszego elementu) izip()
tworzy listę krotek natychmiast po wywołaniu, w sumie tworzone są trzy kopie listy. Jeśli Twoja lista jest bardzo duża, możesz preferowaćfrom itertools import izip, islice for current_item, next_item in izip(the_list, islice(the_list, 1, None)): print(current_item, next_item)
który w ogóle nie kopiuje listy.
źródło
the_list[1:]
tworzy się po prostu obiektu wycinka, a nie kopii prawie całej listy - więc technika OP nie jest tak marnotrawna, jak wydaje się to brzmieć.[1:]
tworzy obiekt plasterka (lub prawdopodobnie „1:
”), który jest przekazywany__slice__
na listę, a następnie zwraca kopię zawierającą tylko wybrane elementy. Jednym idiomatycznym sposobem skopiowania listy jestl_copy = l[:]
(co uważam za brzydkie i nieczytelne - wolęl_copy = list(l)
)__slice__
specjalnej metody.the_list[1:]
jest równoważnethe_list[slice(1, None)]
, co z kolei jest równoważnelist.__getitem__(the_list, slice(1, None))
.the_list[1:]
jest tylko płytką kopią, więc zawiera tylko jeden wskaźnik na element listy. Część bardziej wymagająca pamięci jestzip()
sama w sobie, ponieważ utworzy listę jednejtuple
instancji na element listy, z których każda będzie zawierać dwa wskaźniki do dwóch pozycji i dodatkowe informacje. Ta lista zajmie dziewięć razy więcej pamięci niż kopia spowodowana przez[1:]
użycie.Właśnie to publikuję, jestem bardzo zaskoczony, że nikt nie pomyślał o enumerate ().
for (index, thing) in enumerate(the_list): if index < len(the_list): current, next_ = thing, the_list[index + 1] #do something
źródło
if
można również usunąć, jeśli używasz krojenia:for (index, thing) in enumerate(the_list[:-1]): current, next_ = thing, the_list[index + 1]
Iterowanie według indeksu może zrobić to samo:
#!/usr/bin/python the_list = [1, 2, 3, 4] for i in xrange(len(the_list) - 1): current_item, next_item = the_list[i], the_list[i + 1] print(current_item, next_item)
Wynik:
(1, 2) (2, 3) (3, 4)
źródło
i
zawsze był to indeks aktualnego elementu.To jest teraz prosty import z 16 maja 2020 r
from more_itertools import pairwise for current, next in pairwise(your_iterable): print(f'Current = {current}, next = {nxt}')
Dokumenty dla more-itertools Pod maską ten kod jest taki sam jak w innych odpowiedziach, ale zdecydowanie wolę importować, gdy jest dostępny.
Jeśli jeszcze go nie masz, to:
pip install more-itertools
Przykład
Na przykład, gdybyś miał sekwencję Fibbonnacciego, mógłbyś obliczyć stosunki kolejnych par jako:
from more_itertools import pairwise fib= [1,1,2,3,5,8,13] for current, nxt in pairwise(fib): ratio=current/nxt print(f'Curent = {current}, next = {nxt}, ratio = {ratio} ')
źródło
Pary z listy przy użyciu rozumienia listy
the_list = [1, 2, 3, 4] pairs = [[the_list[i], the_list[i + 1]] for i in range(len(the_list) - 1)] for [current_item, next_item] in pairs: print(current_item, next_item)
Wynik:
(1, 2) (2, 3) (3, 4)
źródło
Jestem naprawdę zaskoczony, że nikt nie wspomniał o krótszej, prostszej i co najważniejsze ogólnej rozwiązaniu:
Python 3:
from itertools import islice def n_wise(iterable, n): return zip(*(islice(iterable, i, None) for i in range(n)))
Python 2:
from itertools import izip, islice def n_wise(iterable, n): return izip(*(islice(iterable, i, None) for i in xrange(n)))
Działa dla iteracji parami przez przekazanie
n=2
, ale może obsłużyć każdą większą liczbę:>>> for a, b in n_wise('Hello!', 2): >>> print(a, b) H e e l l l l o o ! >>> for a, b, c, d in n_wise('Hello World!', 4): >>> print(a, b, c, d) H e l l e l l o l l o l o W o W o W o r W o r l o r l d r l d !
źródło
Podstawowe rozwiązanie:
def neighbors( list ): i = 0 while i + 1 < len( list ): yield ( list[ i ], list[ i + 1 ] ) i += 1 for ( x, y ) in neighbors( list ): print( x, y )
źródło
code = '0016364ee0942aa7cc04a8189ef3' # Getting the current and next item print [code[idx]+code[idx+1] for idx in range(len(code)-1)] # Getting the pair print [code[idx*2]+code[idx*2+1] for idx in range(len(code)/2)]
źródło