这样分页是对的吗,还是有更好的算法?
我想要处理一个像这样的序列:
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
)。
因为你实际上希望最后的元组是被截断的,而不是填满的,所以你需要把最后一个元组中的多余填充值“修剪”掉。