Is er een leuke Python-manier om een lijst te herhalen en een paar elementen opnieuw af te stemmen? Het laatste element moet worden gekoppeld aan het eerste.
Dus als ik bijvoorbeeld de lijst [1, 2, 3] heb, wil ik de volgende paren:
- 1 – 2
- 2 – 3
- 3 – 1
Antwoord 1, autoriteit 100%
Een Pythonische manier om een lijst paarsgewijs te openen is: zip(L, L[1:])
. Om het laatste item met het eerste te verbinden:
>>> L = [1, 2, 3]
>>> zip(L, L[1:] + L[:1])
[(1, 2), (2, 3), (3, 1)]
Antwoord 2, autoriteit 43%
Ik zou een deque
gebruiken met zip
om dit te bereiken.
>>> from collections import deque
>>>
>>> l = [1,2,3]
>>> d = deque(l)
>>> d.rotate(-1)
>>> zip(l, d)
[(1, 2), (2, 3), (3, 1)]
Antwoord 3, autoriteit 41%
Ik zou een kleine wijziging aanbrengen in het pairwise
-recept uit de itertools
documentatie:
def pairwise_circle(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ... (s<last>,s0)"
a, b = itertools.tee(iterable)
first_value = next(b, None)
return itertools.zip_longest(a, b,fillvalue=first_value)
Hiermee blijft gewoon een verwijzing naar de eerste waarde behouden en wanneer de tweede iterator is uitgeput, zal zip_longest
de laatste plaats vullen met de eerste waarde.
(Houd er rekening mee dat het werkt met iterators zoals generatoren en iterables zoals lijsten/tupels.)
Merk op dat @Barry’s oplossinghier erg op lijkt, maar naar mijn mening iets gemakkelijker te begrijpen en gemakkelijker uit te breiden is voorbij één element.
Antwoord 4, autoriteit 33%
Ik zou itertools.cycle
met zip
:
import itertools
def circular_pairwise(l):
second = itertools.cycle(l)
next(second)
return zip(l, second)
cycle
retourneert een iterabele die de waarden van zijn argument in volgorde oplevert, in een lus van de laatste waarde naar de eerste.
We slaan de eerste waarde over, dus deze begint op positie 1
(in plaats van 0
).
Vervolgens zip
we het met de originele, niet-gemuteerde lijst. zip
is goed, omdat het stopt wanneer een van zijn argument-iterables is uitgeput.
Als je het op deze manier doet, vermijd je het maken van tussenliggende lijsten: cycle
bevat een verwijzing naar het origineel, maar kopieert het niet. zip
werkt op dezelfde manier.
Het is belangrijk op te merken dat dit niet werkt als de invoer een iterator
is, zoals een file
, (of een map
of zip
in python-3 ), aangezien het vooruitgaan op één plaats (via next(second)
) automatisch de iterator in alle andere zal bevorderen. Dit is eenvoudig op te lossen met itertools.tee
, die twee onafhankelijk werkende iterators produceert over de originele iterable:
def circular_pairwise(it):
first, snd = itertools.tee(it)
second = itertools.cycle(snd)
next(second)
return zip(first, second)
tee
kangrote hoeveelheden extra opslagruimte gebruiken, bijvoorbeeld als een van de geretourneerde iterators is opgebruikt voordat de andere wordt aangeraakt, maar zoals we alleen ooit hebben één stap verschil, de extra opslagruimte is minimaal.
Antwoord 5, autoriteit 25%
Er zijn efficiëntere manieren (die geen tijdelijke lijsten maken), maar ik denk dat dit de meest beknopte is:
> l = [1,2,3]
> zip(l, (l+l)[1:])
[(1, 2), (2, 3), (3, 1)]
Antwoord 6, autoriteit 19%
Paarsgewijs cirkelvormige Python ‘for’-lus
Als je het geaccepteerde antwoord leuk vindt,
zip(L, L[1:] + L[:1])
je kunt veel meer geheugen gebruiken met semantisch dezelfde code met behulp van itertools
:
from itertools import islice, chain #, izip as zip # uncomment if Python 2
En dit materialiseert nauwelijks iets in het geheugen buiten de originele lijst (ervan uitgaande dat de lijst relatief groot is):
zip(l, chain(islice(l, 1, None), islice(l, None, 1)))
Om te gebruiken, gewoon consumeren (bijvoorbeeld met een lijst):
>>> list(zip(l, chain(islice(l, 1, None), islice(l, None, 1))))
[(1, 2), (2, 3), (3, 1)]
Dit kan uitbreidbaar worden gemaakt tot elke breedte:
def cyclical_window(l, width=2):
return zip(*[chain(islice(l, i, None), islice(l, None, i)) for i in range(width)])
en gebruik:
>>> l = [1, 2, 3, 4, 5]
>>> cyclical_window(l)
<itertools.izip object at 0x112E7D28>
>>> list(cyclical_window(l))
[(1, 2), (2, 3), (3, 4), (4, 5), (5, 1)]
>>> list(cyclical_window(l, 4))
[(1, 2, 3, 4), (2, 3, 4, 5), (3, 4, 5, 1), (4, 5, 1, 2), (5, 1, 2, 3)]
Onbeperkte generatie met itertools.tee
met cycle
U kunt ook tee
gebruiken om te voorkomen dat u een redundant cyclusobject maakt:
from itertools import cycle, tee
ic1, ic2 = tee(cycle(l))
next(ic2) # must still queue up the next item
en nu:
>>> [(next(ic1), next(ic2)) for _ in range(10)]
[(1, 2), (2, 3), (3, 1), (1, 2), (2, 3), (3, 1), (1, 2), (2, 3), (3, 1), (1, 2)]
Dit is ongelooflijk efficiënt, een verwacht gebruik van iter
met next
en elegant gebruik van cycle
, tee
, en zip
.
Geef cycle
niet rechtstreeks door aan list
, tenzij u uw werk hebt opgeslagen en tijd hebt om uw computer stil te laten staan omdat u het maximale uit het geheugen hebt gehaald – als je hebt geluk, na een tijdje zal je besturingssysteem het proces beëindigen voordat het je computer laat crashen.
Pure Python ingebouwde functies
Eindelijk, geen standaard lib-import, maar dit werkt alleen voor de lengte van de originele lijst (anders IndexError.)
>>> [(l[i], l[i - len(l) + 1]) for i in range(len(l))]
[(1, 2), (2, 3), (3, 1)]
Je kunt dit voortzetten met modulo:
>>> len_l = len(l)
>>> [(l[i % len_l], l[(i + 1) % len_l]) for i in range(10)]
[(1, 2), (2, 3), (3, 1), (1, 2), (2, 3), (3, 1), (1, 2), (2, 3), (3, 1), (1, 2)]
Antwoord 7, autoriteit 18%
Ik zou een lijstbegrip gebruiken en profiteren van het feit dat l[-1]
het laatste element is.
>>> l = [1,2,3]
>>> [(l[i-1],l[i]) for i in range(len(l))]
[(3, 1), (1, 2), (2, 3)]
Je hebt op die manier geen tijdelijke lijst nodig.
Antwoord 8, autoriteit 17%
Verbazingwekkend hoeveel verschillende manieren er zijn om dit probleem op te lossen.
Hier is er nog een. Je kunt het recept pairwise
gebruiken, maar in plaats van te zippen met b
, chain
het met het eerste element dat je er al uit hebt gehaald. We hoeven niet te cycle
als we maar één extra waarde nodig hebben:
from itertools import chain, izip, tee
def pairwise_circle(iterable):
a, b = tee(iterable)
first = next(b, None)
return izip(a, chain(b, (first,)))
Antwoord 9, autoriteit 10%
Ik hou van een oplossing die de originele lijst niet wijzigt en de lijst niet naar tijdelijke opslag kopieert:
def circular(a_list):
for index in range(len(a_list) - 1):
yield a_list[index], a_list[index + 1]
yield a_list[-1], a_list[0]
for x in circular([1, 2, 3]):
print x
Uitvoer:
(1, 2)
(2, 3)
(3, 1)
Ik kan me voorstellen dat dit wordt gebruikt voor een aantal zeer grote gegevens in het geheugen.
Antwoord 10, autoriteit 7%
Deze werkt zelfs als de lijst l
het grootste deel van het systeemgeheugen heeft verbruikt. (Als iets garandeert dat dit geval onmogelijk is, dan is zip zoals gepost door chepner prima)
l.append( l[0] )
for i in range( len(l)-1):
pair = l[i],l[i+1]
# stuff involving pair
del l[-1]
of meer algemeen (werkt voor elke offset n
d.w.z. l[ (i+n)%len(l) ]
)
for i in range( len(l)):
pair = l[i], l[ (i+1)%len(l) ]
# stuff
op voorwaarde dat je op een systeem zit met behoorlijk snelle modulo-deling (d.w.z. niet een of ander ingebed systeem met een erwtbrein).
Er lijkt een veelgehoorde overtuiging te zijn dat het indexeren van een lijst met een integer subscript niet-pythonisch is en het best vermeden kan worden. Waarom?
Antwoord 11, autoriteit 6%
Dit is mijn oplossing, en het ziet er volgens mij Pythonisch genoeg uit:
l = [1,2,3]
for n,v in enumerate(l):
try:
print(v,l[n+1])
except IndexError:
print(v,l[0])
afdrukken:
1 2
2 3
3 1
De generatorfunctieversie:
def f(iterable):
for n,v in enumerate(iterable):
try:
yield(v,iterable[n+1])
except IndexError:
yield(v,iterable[0])
>>> list(f([1,2,3]))
[(1, 2), (2, 3), (3, 1)]
Antwoord 12, autoriteit 4%
Wat dacht je hiervan?
li = li+[li[0]]
pairwise = [(li[i],li[i+1]) for i in range(len(li)-1)]
Antwoord 13, autoriteit 4%
from itertools import izip, chain, islice
itr = izip(l, chain(islice(l, 1, None), islice(l, 1)))
(Zoals hierboven met @j-f-sebastian’s “zip”-antwoord, maar met itertools.)
NB: BEWERKTgegeven nuttige zetje van @200_success. was eerder:
itr = izip(l, chain(l[1:], l[:1]))
Antwoord 14, autoriteit 3%
Als je niet te veel geheugen wilt gebruiken, kun je mijn oplossing proberen:
[(l[i], l[(i+1) % len(l)]) for i, v in enumerate(l)]
Het is iets langzamer, maar verbruikt minder geheugen.
Antwoord 15, autoriteit 2%
Nog een poging
>>> L = [1,2,3]
>>> zip(L,L[1:]) + [(L[-1],L[0])]
[(1, 2), (2, 3), (3, 1)]
Antwoord 16, autoriteit 2%
Vanaf Python 3.10
, de nieuwe pairwise
-functie biedt een manier om glijdende paren van opeenvolgende elementen te maken:
from itertools import pairwise
# l = [1, 2, 3]
list(pairwise(l + l[:1]))
# [(1, 2), (2, 3), (3, 1)]
of gewoon pairwise(l + l[:1])
als je het resultaat niet nodig hebt als list
.
Merk op dat we pairwise
op de lijst toegevoegd met zijn kop (l + l[:1]
), zodat rollende paren cirkelvormig zijn (dwz zodat we ook het (3, 1)
paar):
list(pairwise(l)) # [(1, 2), (2, 3)]
l + l[:1] # [1, 2, 3, 1]
Antwoord 17
L = [1, 2, 3]
a = zip(L, L[1:]+L[:1])
voor ik in een:
b = lijst(i)
afdrukken b
Antwoord 18
dit lijkt erop dat combinaties het werk zouden doen.
from itertools import combinations
x=combinations([1,2,3],2)
dit zou een generator opleveren. dit kan dan als zodanig worden herhaald
for i in x:
print i
de resultaten zouden er ongeveer zo uitzien
(1, 2)
(1, 3)
(2, 3)