[英]How to modify elements of iterables with iterators? I.e. how to get write-iterators in Python?
[英]How to write a pager for Python iterators?
我正在尋找一種“翻閱”Python 迭代器的方法。 也就是說,我想用另一個迭代器包裝給定的迭代器iter和page_size ,該迭代器將從 iter 返回項目作為一系列“頁面”。 每個頁面本身就是一個迭代器,最多有page_size次迭代。
我查看了itertools ,我看到的最接近的是itertools.islice 。 在某些方面,我想要的是itertools.chain的對立面—— 與其將一系列迭代器鏈接到一個迭代器中,不如將一個迭代器分解為一系列較小的迭代器。 我期待在 itertools 中找到一個分頁功能,但找不到。
我想出了以下尋呼機類和演示。
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)
我正在尋找一些反饋並有以下問題:
謝謝! -雷蒙德
查看grouper()
,來自itertools
recipes 。
from itertools import zip_longest
def grouper(iterable, n, fillvalue=None):
"Collect data into fixed-length chunks or blocks"
# grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx"
args = [iter(iterable)] * n
return zip_longest(*args, fillvalue=fillvalue)
你為什么不用這個?
def grouper( page_size, iterable ):
page= []
for item in iterable:
page.append( item )
if len(page) == page_size:
yield page
page= []
yield page
“每個頁面本身就是一個迭代器,最多包含 page_size”項。 每個頁面都是一個簡單的項目列表,它是可迭代的。 您可以使用yield iter(page)
來生成迭代器而不是對象,但我看不出這如何改進任何東西。
它在最后拋出一個標准的StopIteration
。
你還想要什么?
我會這樣做:
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)
這樣, None
可以是迭代器吐出的合法值。 只過濾掉了單個對象的fillvalue
,它不可能是可迭代的元素。
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
基於指向 grouper() 的 itertools 配方的指針,我想出了 grouper() 的以下改編來模仿 Pager。 我想過濾掉任何 None 結果並想返回一個迭代器而不是一個元組(盡管我懷疑進行這種轉換可能沒有什么好處)
# 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))
我歡迎關於如何改進此代碼的反饋。
more_itertools.chunked將完全符合您的要求:
>>> import more_itertools
>>> list(chunked([1, 2, 3, 4, 5, 6], 3))
[[1, 2, 3], [4, 5, 6]]
如果您希望在不創建臨時列表的情況下進行分塊,則可以使用more_itertools.ichunked
。
該庫還有許多其他不錯的選項,可用於有效分組、開窗、切片等。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.