我正在寻找一种“分页”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://dev59.com/x3M_5IYBdhLWcg3wslfs
# 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)
我正在寻求一些反馈,有以下问题:
- 是否已经在 itertools 中有一个我忽略的分页器?
- 对 self.iter 进行三次克隆似乎很笨拙。有一个克隆是为了检查 self.iter 是否还有更多项目。我决定采用Alex Martelli建议的技术(知道他写过包装技术)。第二个克隆是为了使返回的页面与内部迭代器(self.iter)独立。是否有方法可以避免克隆3次?
- 除了捕获并再次引发它之外,是否有更好的处理 StopIteration 异常的方法?我倾向于根本不捕获它,让它冒泡。
谢谢! -Raymond