2010-04-13 20 views
36

Czuję, że spędzam dużo czasu na pisaniu kodu w Pythonie, ale nie starcza czasu na tworzenie kodu Python. Ostatnio natknąłem się na zabawny mały problem, który moim zdaniem może mieć łatwe, idiomatyczne rozwiązanie. Parafrazując oryginał, potrzebowałem zebrać każdą kolejną parę na liście. Na przykład, biorąc pod uwagę listę [1,2,3,4,5,6], chciałem obliczyć [(1,2),(3,4),(5,6)].Python "Każdy inny element" Idiom

Wpadłem na szybkie rozwiązanie w czasie, które wyglądało jak przetłumaczona Java. Wracając do pytania, co mogłem zrobić, to

l = [1,2,3,4,5,6] 
[(l[2*x],l[2*x+1]) for x in range(len(l)/2)] 

która ma efekt uboczny rzucając się ostatni numer w przypadku, gdy długość nie jest równa.

Czy istnieje bardziej idiomatyczne podejście, którego mi brakuje, czy jest to najlepsze, co dostanę?

+0

związanych z „Co to jest najbardziej«pythonic»sposobem iteracyjne nad listy w kawałki?” http://stackoverflow.com/questions/434287/what-is-to-most-pythonic-way-to-iterate-over-a-list-in-chunks – jfs

Odpowiedz

78

to zrobi to nieco bardziej elegancko:

>>> data = [1,2,3,4,5,6] 
>>> zip(data[0::2], data[1::2]) 
[(1, 2), (3, 4), (5, 6)] 

(ale to zapewne mniej czytelny, jeśli nie jesteś zaznajomiony z „stride” cechą zakresach).

Podobnie jak kod, odrzuca on ostatnią wartość, gdy masz nieparzystą liczbę wartości.

+3

Uwielbiam funkcję 'zip'. –

+2

Możesz zrobić "dane + = [Brak]", zanim wykonasz powyższe czynności, aby obsłużyć scenariusz nieparzystej liczby pozycji. – badp

+1

'some_list + = [foo]' jest napisane 'some_list.append (foo)'. –

8

Jak na temat korzystania z funkcji stopniową range():

[(l[n],l[n+1]) for n in range(0,len(l),2)] 
48

Jeden często cytowany jest:

zip(*[iter(l)] * 2) 
+8

, ale brzmi jak perl; -) –

+0

To prawda, ale wydaje mi się, że jest to również najbardziej efektywne rozwiązanie przedstawione do tej pory. Testuję, brb. – jemfinch

+1

Tak, jest szybszy niż rozwiązanie RichieHindle o około 10%, ponieważ nie wymaga żadnej alokacji pamięci i tylko jednej iteracji na liście wejściowej. – jemfinch

4

spróbować

def pairs(l, n): 
    return zip(*[l[i::n] for i in range(n)]) 

Więc

pairs([1, 2, 3, 4], 2) daje

[(1, 2), (3, 4)] 
9

I zazwyczaj skopiować grouper receptury z dokumentacją itertools do mojego kodu do tego.

def grouper(n, iterable, fillvalue=None): 
    "grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx" 
    args = [iter(iterable)] * n 
    return izip_longest(fillvalue=fillvalue, *args) 
+1

+1 za wzmiankę o instrukcji F :) – tzot

3

Jeśli nie chcesz stracić elementy, jeśli ich liczba w liście nie jest nawet spróbować tego:

>>> l = [1, 2, 3, 4, 5] 
>>> [(l[i], l[i+1] if i+1 < len(l) else None) for i in range(0, len(l), 2)] 
[(1, 2), (3, 4), (5, None)] 
4

słuszne jest prawdopodobnie nie do obliczenia list, ale napisać iterator- > funkcja iteratora. Jest to bardziej ogólne - działa na każdej iteracji, a jeśli chcesz "zamrozić" ją na liście, możesz użyć funkcji "list()".

def groupElements(iterable, n): 
    # For your case, you can hardcode n=2, but I wanted the general case here. 
    # Also, you do not specify what to do if the 
    # length of the list is not divisible by 2 
    # I chose here to drop such elements 
    source = iter(iterable) 
    while True: 
     l = [] 
     for i in range(n): 
      l.append(source.next()) 
     yield tuple(l) 

Dziwię moduł itertools nie mają już funkcję, która - być może przyszłej rewizji. Do tego czasu zapraszam do korzystania z powyższej wersji :)

+0

Co to jest pod względem wydajności? – LondonRob

4

toolz to dobrze zbudowana biblioteka z wieloma funkcyjnymi niuansami programowania pominiętymi w itertools.partition rozwiązuje ten problem (z opcją pad ostatni wpis na listach długości nieparzyste)

>>> list(toolz.partition(2, [1,2,3,4,5,6])) 
[(1, 2), (3, 4), (5, 6)] 
Powiązane problemy