Python'da bir listeyi çift (mevcut, sonraki) olarak yineleyin


130

Bazen Python'da "geçerli" öğeye ve "sonraki" öğeye bakarak bir listeyi yinelemem gerekir. Şimdiye kadar bunu aşağıdaki gibi bir kodla yaptım:

for current, next in zip(the_list, the_list[1:]):
    # Do something

Bu işe yarıyor ve beklediğim şeyi yapıyor, ancak aynı şeyi yapmanın daha deyimsel veya etkili bir yolu var mı?


Bu soru için MizardX yanıtını kontrol edin . Ama bu çözümün seninkinden daha aptalca olduğunu düşünmüyorum.
Fábio Diniz


39
Bundan başka kimse bahsetmediğinden, o adam olacağım ve nextbu şekilde kullanmanın yerleşik bir şeyi maskelediğine işaret edeceğim .
gönderen

@senderle Belki de Python 2'dir…
Quintec 14

2
@ thecoder16: nextayrıca Python 2'de yerleşik bir işlevdir.
zondo

Yanıtlar:


131

İşte itertools modül belgelerinden alakalı bir örnek :

import itertools
def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = itertools.tee(iterable)
    next(b, None)
    return zip(a, b)   

Python 2 için ihtiyacınız itertools.izipyerine zip:

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)

Bu nasıl çalışır:

İlk olarak, iki paralel yineleyiciler, ave b(oluşturulur tee()çağrı), orijinal iterable ilk elemana hem işaret. İkinci yineleyici, b1 adım ileri ( next(b, None)çağrı) taşınır . Bu noktada as0'ı gösterir ve bs1'i gösterir. Hem ave bbağımsız bir şekilde, orijinal yineleyici geçebilir - izip işlevi iki tekrarlayıcılara alır ve aynı hızda iki yineleyici ilerleyen dönen elemanların çiftleri yapar.

Bir uyarı: tee()İşlev, birbirinden bağımsız olarak ilerleyebilen iki yineleyici üretir, ancak bir bedeli vardır. Yineleyicilerden biri diğerinden daha fazla tee() ilerlerse, ikinci yineleyici de onları dinleyene kadar tüketilen öğeleri bellekte tutması gerekir (orijinal yineleyiciyi 'geri saramaz'). Burada önemli değil çünkü bir yineleyici diğerinden sadece 1 adım önde, ancak genel olarak bu şekilde çok fazla bellek kullanmak kolaydır.

Ve tee()bir nparametre alabildiğinden, bu aynı zamanda ikiden fazla paralel yineleyici için de kullanılabilir:

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)

4
Örnek kod harika ... ama neden işe yaradığına dair biraz açıklama yapabilir misin? Mesela "tee ()" ve "next ()" burada ne yaptığını söyle.
John Mulder

@John Mulder: Kısa bir özet yaptı.
Rafał Dowgird

9
zip(ł, ł[1:])çok daha kısa ve
pitonik

2
@ noɥʇʎԀʎzɐɹƆ: Hayır, her yinelenebilir üzerinde çalışmaz ve listelerde kullanıldığında gereksiz bir kopya oluşturur. Fonksiyonları kullanmak pitoniktir.
Ry-

Uygulanan Bu fonksiyon funcymodülü: funcy.pairwise: funcy.readthedocs.io/en/stable/seqs.html#pairwise
ADR

30

Kendininkini yuvarla!

def pairwise(iterable):
    it = iter(iterable)
    a = next(it, None)

    for b in it:
        yield (a, b)
        a = b

1
Tam ihtiyacım olan şey! Bu bir python yöntemi olarak ölümsüzleştirildi mi yoksa devam etmemiz gerekiyor mu?
uhoh

1
@uhoh: Bildiğim kadarıyla henüz değil!
Ry-

21

Yana the_list[1:]aslında (ilk unsuru hariç) bütün listesinin bir kopyasını oluşturur ve zip()listenizin toplam üç nüsha olarak adlandırılan hemen dizilerini bir liste oluşturur oluşturulur. Listeniz çok büyükse, tercih edebilirsiniz

from itertools import izip, islice
for current_item, next_item in izip(the_list, islice(the_list, 1, None)):
    print(current_item, next_item)

bu listeyi hiç kopyalamaz.


3
python 3.x'te izip'in itertools tarafından bastırıldığını ve yerleşik zip kullanmanız gerektiğini unutmayın
Xavier Combelle

1
Aslında, the_list[1:]neredeyse tüm listenin bir kopyası olmaktan ziyade sadece bir dilim nesnesi oluşturmakla kalmaz - bu nedenle OP'nin tekniği, ses çıkardığınız kadar savurgan değildir.
martineau

3
Bence [1:](ya da muhtemelen "dilim nesnesi yaratır 1:") geçirilir hangi __slice__sonra sadece seçilen öğeleri içeren bir kopyasını döndürür listede bulunuyordu. Bir listeyi kopyalamanın deyimsel bir yolu l_copy = l[:](çirkin ve okunamaz buluyorum - tercih ederim l_copy = list(l))
dcrosta

4
@dcrosta: __slice__Özel bir yöntem yok . the_list[1:]eşdeğerdir the_list[slice(1, None)], bu da eşdeğerdir list.__getitem__(the_list, slice(1, None)).
Sven Marnach

4
@martineau: tarafından oluşturulan kopya the_list[1:]yalnızca yüzeysel bir kopyadır, bu nedenle liste öğesi başına yalnızca bir işaretçiden oluşur. Daha fazla bellek yoğun bölüm zip()kendisidir, çünkü tupleher biri iki öğe için iki işaretçi ve bazı ek bilgiler içeren liste öğesi başına bir örnek listesi oluşturacaktır . Bu liste, kopyanın neden olduğu bellek miktarının dokuz katını tüketecektir [1:].
Sven Marnach

19

Bunu sadece ortaya koyuyorum, kimsenin numaralandırmayı () düşünmemesine çok şaşırdım.

for (index, thing) in enumerate(the_list):
    if index < len(the_list):
        current, next_ = thing, the_list[index + 1]
        #do something

11
Aslında, ifdilimlemeyi kullanırsanız da kaldırılabilir:for (index, thing) in enumerate(the_list[:-1]): current, next_ = thing, the_list[index + 1]
lifebalance

2
Bu gerçekten cevap olmalı, herhangi bir ekstra ithalata dayanmıyor ve harika çalışıyor.
jamescampbell

Yine de, endekslenemeyen yinelemeler için çalışmaz, bu nedenle genel bir çözüm değildir.
wim

14

Dizine göre yinelemek aynı şeyi yapabilir:

#!/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)

Çıktı:

(1, 2)
(2, 3)
(3, 4)

Cevabınız, soruda olduğu gibi , güncel ve sonraki yerine daha önceki ve günceldi . Anlamsallığı geliştiren bir düzenleme yaptım, böylece her zaman mevcut elemanın indeksi olur. i
Bengt

1

Bu artık 16 Mayıs 2020 itibarıyla basit bir İthalattır

from more_itertools import pairwise
for current, next in pairwise(your_iterable):
  print(f'Current = {current}, next = {nxt}')

Daha fazla itertools için Dokümanlar bu kod diğer yanıtlardakiyle aynıdır, ancak mevcut olduğunda ithalatı daha çok tercih ederim.

Henüz yüklemediyseniz, o zaman: pip install more-itertools

Misal

Örneğin, fibbonnacci dizisine sahipseniz, sonraki çiftlerin oranlarını şu şekilde hesaplayabilirsiniz:

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

0

Liste anlama kullanarak listeden çiftler

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)

Çıktı:

(1, 2)
(2, 3)
(3, 4)

0

Kimsenin daha kısa, daha basit ve en önemlisi genel çözümden bahsetmemesine gerçekten şaşırdım :

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

Geçerek ikili yineleme için çalışır n=2, ancak daha yüksek sayıları işleyebilir:

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

-2

Temel bir çözüm:

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 )

-2
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)]
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.