2010-09-19 16 views
7

Voglio essere in grado di prendere una sequenza del tipo:È così che si impagina o c'è un algoritmo migliore?

my_sequence = ['foo', 'bar', 'baz', 'spam', 'eggs', 'cheese', 'yogurt'] 

Utilizzare una funzione come:

my_paginated_sequence = get_rows(my_sequence, 3) 

Per ottenere risultati:

[['foo', 'bar', 'baz'], ['spam', 'eggs', 'cheese'], ['yogurt']] 

Questo è quello che mi è venuta da solo a pensarci:

def get_rows(sequence, num): 
    count = 1 
    rows = list() 
    cols = list() 
    for item in sequence: 
     if count == num: 
      cols.append(item) 
      rows.append(cols) 
      cols = list() 
      count = 1 
     else: 
      cols.append(item) 
      count += 1 
    if count > 0: 
     rows.append(cols) 
    return rows 
+2

@Noon - thx, non ha pensato di aggiungere quello. Inoltre, sei un ninja per caso? – orokusaki

+0

possibile duplicato di [Rendi più oggetti alla volta da un oggetto iterabile?] (Http://stackoverflow.com/questions/2202461/yield-multiple-objects-at-a-time-from-an-iterable-object) –

+0

possibile duplicato di [Come si divide una lista in blocchi di dimensioni uguali in Python?] (Http://stackoverflow.com/q/312443/54262) –

risposta

11

Se sai di avere una sequenza affettabili (lista o tupla),

def getrows_byslice(seq, rowlen): 
    for start in xrange(0, len(seq), rowlen): 
     yield seq[start:start+rowlen] 

Questo, naturalmente, è un generatore, quindi se hai assolutamente bisogno di un elenco come risultato, userai lo list(getrows_byslice(seq, 3)) o simili, ovviamente.

Se si inizia con un iterabile generica, l'aiuto itertools recipes offerta con la ricetta grouper ...:

import itertools 

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

(ancora una volta, è necessario chiamare list su questo se una lista è quello vuoi, ovviamente).

Poiché in realtà si desidera che l'ultima tupla venga troncata anziché riempita, è necessario "tagliare" i valori di riempimento finali dall'ultima tupla.

+0

vedere cosa intendo riguardo Jedi. Sento che non sarò mai in grado di fare cose del genere direttamente dal mio cervello. Ti sei mai sentito così in passato? – orokusaki

+0

@orokusaki, ovviamente - ma poi ho iniziato a leggere i documenti (ricorda che la funzione 'cernia' è citata direttamente dai documenti! -). –

+1

Inoltre, sto parlando con mio fratello al telefono di quanto sia utile per la comunità Python nel suo complesso. Ci chiediamo entrambi, cos'è che spinge il tuo entusiasmo ad aiutare gli altri?Spero che un giorno possa essere come te riguardo a questa roba o qualsiasi altra cosa che farò in futuro. – orokusaki

0

Se siete alla ricerca per la lista verso l'alto di comprensione, questo farà il lavoro:

L = ['foo', 'bar', 'baz', 'spam', 'eggs', 'cheese', 'yogurt'] 
[L[i*3 : (i*3)+3] for i in range((len(L)/3)+1) if L[i*3 : (i*3)+3]] 
# [['foo', 'bar', 'baz'], ['spam', 'eggs', 'cheese'], ['yogurt']] 
L = ['foo', 'bar', 'baz', 'spam', 'eggs', 'cheese'] 
# [['foo', 'bar', 'baz'], ['spam', 'eggs', 'cheese']] 
6

Questa versione funziona con qualsiasi (possibilmente pigro e non affettabili) iterable e produce un pigro iterabile (in altre parole, è un generatore e funziona con tutti i tipi di sequenze, compresi altri generatori):

import itertools 

def paginate(iterable, page_size): 
    while True: 
     i1, i2 = itertools.tee(iterable) 
     iterable, page = (itertools.islice(i1, page_size, None), 
       list(itertools.islice(i2, page_size))) 
     if len(page) == 0: 
      break 
     yield page 

Alcuni esempi:

In [61]: list(paginate(my_sequence, 3)) 
Out[61]: [['foo', 'bar', 'baz'], ['spam', 'eggs', 'cheese'], ['yogurt']] 

In [62]: list(paginate(xrange(10), 3)) 
Out[62]: [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]] 
1

La funzione grouper nei documenti itertools è intelligente e concisa; l'unico problema è che potrebbe essere necessario tagliare i risultati, come ha sottolineato Alex Martelli. Sarei propenso a una soluzione sulla falsariga della risposta di Michał Marczyk, anche se non vedo perché questo non possa essere reso molto più semplice. Questo funziona per tutti i casi che posso immaginare:

import itertools 

def paginate(seq, page_size): 
    i = iter(seq) 
    while True: 
     page = tuple(itertools.islice(i, 0, page_size)) 
     if len(page): 
      yield page 
     else: 
      return