10

Python イテレータを「ページ スルー」する方法を探しています。つまり、指定されたイテレータiterpage_sizeを、一連の「ページ」として iter からアイテムを返す別のイテレータでラップしたいと考えています。各ページ自体が、最大でpage_sizeの反復を行う反復子になります。

私はitertoolsを調べましたが、最も近いものはitertools.isliceです。いくつかの点で、私が望むのはitertools.chainの反対です。一連のイテレータを 1 つのイテレータに連鎖させるのではなく、イテレータを一連の小さなイテレータに分割したいと考えています。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)   

私はいくつかのフィードバックを探しており、次の質問があります。

  1. 私が見落としているページャーを提供するitertoolsに既にページャーがありますか?
  2. self.iter を 3 回複製するのは、私には厄介に思えます。1 つのクローンは、self.iter にさらに項目があるかどうかを確認することです。私はAlex Martelli が提案した手法を使用することにしました (彼がラッピング手法について書いていることに注意してください)。2 番目のクローンは、返されるページを内部反復子 ( self.iter ) から独立できるようにすることでした。3つのクローンを作成しないようにする方法はありますか?
  3. それをキャッチしてから再度発生させる以外に、 StopIteration例外を処理するより良い方法はありますか? 私はそれをまったく捕まえずに泡立たせたいと思っています。

ありがとう!-レイモンド

4

6 に答える 6

8

を見てgrouper()itertoolsレシピから。

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)
于 2010-02-27T17:43:37.833 に答える
4

なんでこれ使わないの?

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ます。

これ以上何が欲しいですか?

于 2010-02-28T11:34:13.490 に答える
3

私は次のようにします:

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が除外され、イテラブルの要素である可能性はありません。

于 2010-02-28T00:15:53.293 に答える
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
于 2014-02-20T11:32:46.687 に答える
0

grouper() の itertools レシピへのポインターに基づいて、Pager を模倣するために grouper() を次のように適応させることにしました。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))

このコードを改善するために何ができるかについてのフィードバックを歓迎します。

于 2010-02-27T22:55:00.133 に答える