2011-08-10 24 views
12

W Pythonie łatwo złamać n -long listy do K -size kawałki jeśli n jest wielokrotnością K (IOW, n % k == 0). Oto mój ulubiony podejście (prosto z docs).Prosty idiom łamiący n-długą listę na k-długie kawałki, gdy n% k> 0?

>>> k = 3 
>>> n = 5 * k 
>>> x = range(k * 5) 
>>> zip(*[iter(x)] * k) 
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 14)] 

(Sztuką jest to, że [iter(x)] * k tworzy listę k odniesień do ten sam iterator, zwracanego przez iter(x) Następnie zip generuje każdy kawałek przez wzywając każdego z k egzemplarzy iterator dokładnie raz. na * przed [iter(x)] * k jest konieczne, ponieważ zip spodziewa się otrzymać swoje argumenty jako „odrębnego” iteratorów raczej niż ich listę.)

Główną wadą widzę z tego idiomu jest to, że kiedy n nie jest wielokrotnością k (IOW, n % k > 0), po lewej stronie nad wpisami są tylko w lewo na zewnątrz; np:

>>> zip(*[iter(x)] * (k + 1)) 
[(0, 1, 2, 3), (4, 5, 6, 7), (8, 9, 10, 11)] 

Jest alternatywą idiom, który jest nieco dłuższy, aby wpisać, daje ten sam wynik, jak ten powyżej, gdy n % k == 0, i ma bardziej akceptowalnego zachowania podczas n % k > 0:

>>> map(None, *[iter(x)] * k) 
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 14)] 
>>> map(None, *[iter(x)] * (k + 1)) 
[(0, 1, 2, 3), (4, 5, 6, 7), (8, 9, 10, 11), (12, 13, 14, None)] 

Przynajmniej tutaj pozostałe wpisy są zachowywane, ale ostatni fragment zostaje dopełniony None. Jeśli ktoś chce po prostu innej wartości dla wypełnienia, to problem rozwiązuje itertools.izip_longest.

Ale załóżmy, że pożądane jest rozwiązanie, w którym ostatnia porcja jest lewy unpadded, tj

[(0, 1, 2, 3), (4, 5, 6, 7), (8, 9, 10, 11), (12, 13, 14)] 

Czy istnieje prosty sposób zmodyfikować map(None, *[iter(x)]*k) idiom do produkcji tego rezultat?

(To prawda, nie jest trudno rozwiązać ten problem, pisząc funkcję (zobacz na przykład wiele drobnych odpowiedzi na How do you split a list into evenly sized chunks? lub What is the most "pythonic" way to iterate over a list in chunks?). Dlatego dokładniejszym tytułem tego pytania będzie "Jak uratować idiom map(None, *[iter(x)]*k)?”, ale myślę, że przegroda wielu czytelników.)

uderzyło mnie, jak łatwo jest złamać listę do nawet wielkości kawałki i jak trudne (w porównaniu!) polega to na pozbyciu się niechcianego obicia, mimo że oba problemy wydają się być porównywalnej złożoności.

+0

Czy pytasz o to z praktycznego powodu, czy po prostu, czy można to zrobić? –

+0

Czy nie jest to duplikat http://stackoverflow.com/questions/312443/how-do-you-split-a-list-into-evenly-sized-chunks-in-python? –

+0

@Ned Batchelder: Próbowałem wyjaśnić, że ten post był jego kontynuacją/rozszerzeniem (w rzeczywistości przytaczam ten sam stackoverflow post na końcu). Ponadto, jak starałem się wyjaśnić na końcu tego posta, ten post jest mniej o rozwiązywaniu problemu kawałkowania (dobre rozwiązania są podane w postach, które cytowałem), ale raczej, aby dowiedzieć się, czy istnieje prosty sposób na rozszerzenie przydatność konkretnego idiomu Pythona. Być może posty wymagają innego tytułu, ale wszystkie, które mogłem wymyślić, wyglądały na zagmatwane ... – kjo

Odpowiedz

15
[x[i:i+k] for i in range(0,n,k)] 
3
sentinal = object() 
split = ( 
    (v for v in r if v is not sentinal) for r in 
    izip_longest(*[iter(x)]*n, fillvalue=sentinal)) 

Oczywiście, lepiej idiom jest wywołanie funkcji, jak to będzie bardziej czytelny wtedy coś, co będziemy robić to samo.

1

Co z tym?To inny idiom, ale daje pożądany rezultat:

[x[i:i+k] for i in range(0,len(x),k)] #=> [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11], [12, 13, 14]] 
[x[i:i+k] for i in range(0,len(x),k)] #=> [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11], [12, 13, 14]] 

Lub jeśli naprawdę potrzebujesz krotki, użyj tuple(x[i:i+k]) zamiast tylko x[i:i+k].

3

od źródła ipython za:

def chop(seq,size): 
    """Chop a sequence into chunks of the given size.""" 
    chunk = lambda i: seq[i:i+size] 
    return map(chunk,xrange(0,len(seq),size)) 

listę ostatnio powrócił będzie mieć mniej niż chunk elementy jeśli sekwencja nie jest podzielny, w zasadzie robi krótki koniec kija, ale bez narzekania.

>>> chop(range(12),3) 
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]] 
>>> chop(range(12),4) 
[[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]] 
>>> chop(range(12),5) 
[[0, 1, 2, 3, 4], [5, 6, 7, 8, 9], [10, 11]] 
>>> chop(range(12),6) 
[[0, 1, 2, 3, 4, 5], [6, 7, 8, 9, 10, 11]] 
Powiązane problemy