这样分页是对的吗,还是有更好的算法?

11 投票
4 回答
22204 浏览
提问于 2025-04-16 04:19

我想要处理一个像这样的序列:

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

使用一个像这样的函数:

my_paginated_sequence = get_rows(my_sequence, 3)

来得到:

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

这是我通过思考得出的结果:

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

4 个回答

3

itertools文档中的grouper函数非常聪明且简洁;唯一的问题是,正如Alex Martelli所指出的,你可能需要对结果进行一些修剪。我倾向于采用Michał Marczyk的答案中的一种解决方案,尽管我觉得可以把它简化得更容易理解。这种方法适用于我能想到的所有情况:

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
7

这个版本可以处理任何类型的可迭代对象(可能是懒加载的,也可能不能切片),并且会生成一个懒加载的可迭代对象(换句话说,它是一个生成器,可以处理所有类型的序列,包括其他生成器):

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

以下是一些例子:

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]]
19

如果你知道你有一个可以切片的序列(比如列表或元组),

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

这当然是一个生成器,所以如果你真的需要一个列表作为结果,你可以用 list(getrows_byslice(seq, 3)) 这样的方式来实现。

如果你开始时的是一个普通的可迭代对象,itertools 的一些方法 可以帮助你使用 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)

(同样,如果你想要一个列表的话,你当然需要在这个结果上调用 list)。

因为你实际上希望最后的元组是被截断的,而不是填满的,所以你需要把最后一个元组中的多余填充值“修剪”掉。

撰写回答