2010-02-27 30 views
9

Bir Python yineleyici ile "sayfa geçişi" yapmanın bir yolunu arıyorum. Yani, belirli bir yineleyici iter ve page_size sayfalarını iter öğesinden bir dizi "sayfa" olarak döndüren başka bir yineleyici ile sarmak istiyorum. Her sayfa, en çok page_size yinelemeli bir yineleyici olacaktır.Python yineleyicileri için nasıl çağrı yazılır?

itertools'u inceledim ve gördüğüm en yakın şey itertools.islice. Bazı şekillerde, ne istersem, itertools.chain'un tam tersidir - bir yineleyici dizisini tek bir yineleyiciye zincirlemek yerine, bir yineleyiciyi bir dizi küçük yineleyiciye bölmek isterim. Itertools'ta bir çağrı fonksiyonu bulmayı bekliyordum ama bir tane bulamadım.

Aşağıdaki çağrı sınıfı ve gösterimi ile geldim.

class pager(object): 
    """ 
    takes the iterable iter and page_size to create an iterator that "pages through" iter. That is, pager returns a series of page iterators, 
    each returning up to page_size items from iter. 
    """ 
    def __init__(self,iter, page_size): 
     self.iter = iter 
     self.page_size = page_size 
    def __iter__(self): 
     return self 
    def next(self): 
     # if self.iter has not been exhausted, return the next slice 
     # I'm using a technique from 
     # https://stackoverflow.com/questions/1264319/need-to-add-an-element-at-the-start-of-an-iterator-in-python 
     # to check for iterator completion by cloning self.iter into 3 copies: 
     # 1) self.iter gets advanced to the next page 
     # 2) peek is used to check on whether self.iter is done 
     # 3) iter_for_return is to create an independent page of the iterator to be used by caller of pager 
     self.iter, peek, iter_for_return = itertools.tee(self.iter, 3) 
     try: 
      next_v = next(peek) 
     except StopIteration: # catch the exception and then raise it 
      raise StopIteration 
     else: 
      # consume the page from the iterator so that the next page is up in the next iteration 
      # is there a better way to do this? 
      # 
      for i in itertools.islice(self.iter,self.page_size): pass 
      return itertools.islice(iter_for_return,self.page_size) 



iterator_size = 10 
page_size = 3 

my_pager = pager(xrange(iterator_size),page_size) 

# skip a page, then print out rest, and then show the first page 
page1 = my_pager.next() 

for page in my_pager: 
    for i in page: 
     print i 
    print "----" 

print "skipped first page: " , list(page1) 

bazı geribildirim arıyorum ve aşağıdaki sorular var:

  1. bir çağrı cihazı ben bakan olduğum bir çağrı cihazını vermektedir itertools zaten var mıdır?
  2. Cloning self.iter 3 kere bana kludgy görünüyor. Bir klon, self.iter'in başka öğelere sahip olup olmadığını kontrol etmektir. a technique Alex Martelli suggested ile gitmeye karar verdim (bir wrapping technique yazdığını farkettim). İkinci klon, döndürülen sayfanın dahili yineleyiciden bağımsız olmasını sağlamaktır (self.iter). 3 klon yapmaktan kaçınmanın bir yolu var mı?
  3. StopIteration istisnasının yakalanması ve daha sonra yeniden yükseltilmesiyle ilgilenmenin daha iyi bir yolu var mı? Onu yakalamama izin verdim ve kabarmasına izin verdim.

teşekkürler! -Raymond

+1

İlgili: http://stackoverflow.com/questions/312443/how-do-you-split-a-list-into-evenly-sized-chunks-in-python http://stackoverflow.com/questions/434287/en-ne-en-pythonic-it-içinde-bir-liste-in-chunks-it-içinde-what-is-it-http://stackoverflow.com/questions/1335392/iteration-over-list-slices http : //stackoverflow.com/questions/760753/iterate-over-a-python-sequence-in-multiples-of-n – jfs

cevap

4

Neden bunu kullanmıyorsunuz?

def grouper(page_size, iterable): 
    page= [] 
    for item in iterable: 
     page.append(item) 
     if len(page) == page_size: 
      yield page 
      page= [] 
    yield page 

"Her sayfa, en çok page_size öğelerine sahip bir yineleyici olabilir". Her sayfa, yinelenebilen öğelerin basit bir listesidir.Nesne yerine yineleyici oluşturmak için yield iter(page)'u kullanabilirsiniz, ancak bunun nasıl bir şey geliştirdiğini göremiyorum.

Sonunda standart bir StopIteration atar.

Daha ne istersiniz?

+0

Sorumu yanıtladığınız için ve yineleyiciyi nasıl döngülendireceğinizi düşünmek için iyi bir yol sağladığınız için teşekkür ederiz. Küçük bir hata olduğunu düşünüyorum - öğeyi sayfaya eklemek istediniz - aşağıdaki gibi: def grubu oluşturucu (page_size, yinelenebilir): sayfa = [] yinelenen öğe için: eğer len (sayfa) == PAGE_SIZE: verim sayfa sayfa = [] başka : page.append (item) verim sayfa –

+0

@raymondyee: Aslında, daha iyi bir yolu var. Sürümünüz büyük. Bir öğeyi atladığını gör ve gör. –

+0

@ S.Lott - evet, elbette, page.append (item) öğesini yanlış yere koydum. Düzeltme için teşekkürler. Hala itertools'un ne zaman ve ne zaman ihtiyaç duyulmadığını öğreniyorum. Sunulacak herhangi bir yönerge? –

7

grouper() adresindeki itertools recipes'a bakın.

+0

Tariflere dikkat ettiğiniz için teşekkürler. Gruplayıcıyı kullanarak görüyorum çünkü verimli ve reçetemi benim Çağrı Cihazım gibi davranması için uyarlıyor. Şu anda Pager'in durduğu gibi olup olmadığını merak ediyorum - ya da gruplayıcı bir yaklaşım için onu terk etmeliyim. –

0

Gruplayıcı() için itertools tarifi işaretçisine dayanarak, Pager'ı taklit etmek için aşağıdaki grouper() uyarlamasıyla geldim. Ben herhangi bir Hiçbiri sonuçlarını filtrelemek için istedi ve (ı bu dönüşümü yaparken küçük bir avantaj olabileceğini şüpheli olsa da) yerine bir başlığın daha

# based on http://docs.python.org/library/itertools.html#recipes 
def grouper2(n, iterable, fillvalue=None): 
    args = [iter(iterable)] * n 
    for item in izip_longest(fillvalue=fillvalue, *args): 
     yield iter(filter(None,item)) 

nasıl elimden geleni hakkında geri bildirim hoşuma gider bir yineleyici dönmek istedim Bu kodu geliştirmek için yapmak.

2

böyle yapardım:

def pager(iterable, page_size): 
    args = [iter(iterable)] * page_size 
    fillvalue = object() 
    for group in izip_longest(fillvalue=fillvalue, *args): 
     yield (elem for elem in group if elem is not fillvalue) 

Bu şekilde, None yineleyici tükürür o meşru bir değer olabilir. Sadece tek bir nesne fillvalue filtrelenmiştir ve muhtemelen yinelenebilir bir unsur olamaz.

+0

Teşekkürler Matt. Her ikisinin de Yok'un yinelemeden okunaklı bir değer olmasına izin vermediğimi ve doldurma değerlerini hesaba katmadığımı fark ettiniz. –

0
def group_by(iterable, size): 
    """Group an iterable into lists that don't exceed the size given. 

    >>> group_by([1,2,3,4,5], 2) 
    [[1, 2], [3, 4], [5]] 

    """ 
    sublist = [] 

    for index, item in enumerate(iterable): 
     if index > 0 and index % size == 0: 
      yield sublist 
      sublist = [] 

     sublist.append(item) 

    if sublist: 
     yield sublist 
İlgili konular