Python3生成器理解生成块,包括最后一个
如果在Python 3.7中有一个列表:Python3生成器理解生成块,包括最后一个,python,python-3.x,generator,Python,Python 3.x,Generator,如果在Python 3.7中有一个列表: >>> li [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 您可以使用两种常见的Python习惯用法之一将其转换为一个块列表,每个块的长度n: >>> n=3 >>> list(zip(*[iter(li)]*n)) [(0, 1, 2), (3, 4, 5), (6, 7, 8)] 从(9,10)开始删除最后一个不完整的元组不是长度n 您还可以执行以下操作: >&g
>>> li
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
您可以使用两种常见的Python习惯用法之一将其转换为一个块列表,每个块的长度n
:
>>> n=3
>>> list(zip(*[iter(li)]*n))
[(0, 1, 2), (3, 4, 5), (6, 7, 8)]
从(9,10)
开始删除最后一个不完整的元组不是长度n
您还可以执行以下操作:
>>> [li[i:i+n] for i in range(0,len(li),n)]
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10]]
如果您想要最后一个子列表,即使它的元素少于n
假设现在我有一个生成器,gen
,未知长度或终止(因此调用list(gen))
或sum(1代表uuu-in-gen)
将不明智),在这里我想要每个块
我能想到的最好的生成器表达式是这样的:
from itertools import zip_longest
sentinel=object() # for use in filtering out ending chunks
gen=(e for e in range(22)) # fill in for the actual gen
g3=(t if sentinel not in t else tuple(filter(lambda x: x != sentinel, t)) for t in zip_longest(*[iter(gen)]*n,fillvalue=sentinel))
为预期目的工作:
>>> next(g3)
(0, 1, 2)
>>> next(g3)
(3, 4, 5)
>>> list(g3)
[(6, 7, 8), (9, 10)]
只是看起来很笨拙。我试过:
islice
但长度不足似乎难以克服李>
iter
中使用哨兵,但是iter
的哨兵版本需要可调用的,而不是可调用的n
的块生成器(包括可能小于n
的最后一个卡盘),是否有一种更惯用的Python 3技术
我也对发电机功能持开放态度。我正在寻找一些惯用的、更具可读性的东西
更新: DSM删除的答案中的方法非常好,我认为:
>>> g3=(iter(lambda it=iter(gen): tuple(islice(it, n)), ()))
>>> next(g3)
(0, 1, 2)
>>> list(g3)
[(3, 4, 5), (6, 7, 8), (9, 10)]
我对这个问题持开放态度,但这个相关的问题已经有近10年的历史了,主要集中在一个列表上。在Python3中,没有一种新的方法可以生成不知道长度并且一次只需要一个块的生成器 我认为,只要你想把它放进一行中,这件事就会变得一团糟。 我会咬紧牙关,在这里使用发电机功能。如果您不知道实际大小(例如,如果
gen
是一个无限大的生成器,等等),则特别有用
有人可能会有更好的建议,但我会这样做。这个带有生成器函数的解决方案非常明确和简短:
def g3(seq):
it = iter(seq)
while True:
head = list(itertools.islice(it, 3))
if head:
yield head
else:
break
这感觉像是一种基于itertools的非常合理的方法
>>> g = (i for i in range(10))
>>> g3 = takewhile(lambda x: x, (list(islice(g,3)) for _ in count(0)))
>>> list(g3)
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]
我尝试使用
groupby
和cycle
。使用cycle
可以选择一种模式来对元素进行分组,因此它的用途非常广泛:
from itertools import groupby, cycle
gen=(e for e in range(11))
d = [list(g) for d, g in groupby(gen, key=lambda v, c=cycle('000111'): next(c))]
print([v for v in d])
产出:
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10]]
文档的部分提供了各种生成器帮助程序
在这里,您可以使用第二种形式的修改take
,以创建块生成器
from itertools import islice
def chunks(n, it):
it = iter(it)
return iter(lambda: tuple(islice(it, n)), ())
例子
输出
我们可以使用itertools文档页面中给出的grouper函数来实现这一点
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(fillvalue=fillvalue, *args)
def out_iterator(lst):
for each in grouper(lst,n):
if None in each:
yield each[:each.index(None)]
else:
yield each
a=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
n=3
print(list(out_iterator(a)))
输出:
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10)]
我在这里列出了一些答案的时间安排 我最初编写它的方式实际上是Python3.7上最快的。对于一艘客轮来说,这可能是最好的 cold speed’s的一个改进版本既快速又通俗易懂 其他答案都是相似的速度 基准:
from __future__ import print_function
try:
from itertools import zip_longest, takewhile, islice, count
except ImportError:
from itertools import takewhile, islice, count
from itertools import izip_longest as zip_longest
from collections import deque
def f1(it,k):
sentinel=object()
for t in (t if sentinel not in t else tuple(filter(lambda x: x != sentinel, t)) for t in zip_longest(*[iter(it)]*k, fillvalue=sentinel)):
yield t
def f2(it,k):
for t in (iter(lambda it=iter(it): tuple(islice(it, k)), ())):
yield t
def f3(it,k):
while True:
chunk = (*islice(it, 0, k),) # tuple(islice(it, 0, k)) if Python < 3.5
if chunk:
yield chunk
else:
break
def f4(it,k):
for t in takewhile(lambda x: x, (tuple(islice(it,k)) for _ in count(0))):
yield t
if __name__=='__main__':
import timeit
def tf(f, k, x):
data=(y for y in range(x))
return deque(f(data, k), maxlen=3)
k=3
for f in (f1,f2,f3,f4):
print(f.__name__, tf(f,k,100000))
for case, x in (('small',10000),('med',100000),('large',1000000)):
print("Case {}, {:,} x {}".format(case,x,k))
for f in (f1,f2,f3,f4):
print(" {:^10s}{:.4f} secs".format(f.__name__, timeit.timeit("tf(f, k, x)", setup="from __main__ import f, tf, x, k", number=10)))
:
另见:
可能我误解了,但是
islice
像gen:print(tuple(islice(gen,3))中的那样的有什么问题呢?
(用yield
替换print
,当然是一个生成器函数)可能重复,@Kasramvd:啊,是的——我的答案只是其中一行有一个默认值,@Kasramvd:我不认为这些是完全重复的,因为1)大部分与内存中已经存在的列表有关,或者2)没有采用Python 3.6+的更新功能,3)与我列出的两个习惯用法有一些不同。相关问题已经有10年历史了。我们是否得出结论,没有新的Python3唯一的方法可以做到这一点?这既清晰又快速。(我对它们进行了基准测试…)谢谢时间安排;-)如果您想要元组列表和列表列表,还可以执行chunk=(*islice(it,0,k),)
。在Python<3.5上,tuple(islice(it,0,k))
(0, 1, 2) (3, 4, 5) (6, 7, 8) (9, 10)
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(fillvalue=fillvalue, *args)
def out_iterator(lst):
for each in grouper(lst,n):
if None in each:
yield each[:each.index(None)]
else:
yield each
a=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
n=3
print(list(out_iterator(a)))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10)]
from __future__ import print_function
try:
from itertools import zip_longest, takewhile, islice, count
except ImportError:
from itertools import takewhile, islice, count
from itertools import izip_longest as zip_longest
from collections import deque
def f1(it,k):
sentinel=object()
for t in (t if sentinel not in t else tuple(filter(lambda x: x != sentinel, t)) for t in zip_longest(*[iter(it)]*k, fillvalue=sentinel)):
yield t
def f2(it,k):
for t in (iter(lambda it=iter(it): tuple(islice(it, k)), ())):
yield t
def f3(it,k):
while True:
chunk = (*islice(it, 0, k),) # tuple(islice(it, 0, k)) if Python < 3.5
if chunk:
yield chunk
else:
break
def f4(it,k):
for t in takewhile(lambda x: x, (tuple(islice(it,k)) for _ in count(0))):
yield t
if __name__=='__main__':
import timeit
def tf(f, k, x):
data=(y for y in range(x))
return deque(f(data, k), maxlen=3)
k=3
for f in (f1,f2,f3,f4):
print(f.__name__, tf(f,k,100000))
for case, x in (('small',10000),('med',100000),('large',1000000)):
print("Case {}, {:,} x {}".format(case,x,k))
for f in (f1,f2,f3,f4):
print(" {:^10s}{:.4f} secs".format(f.__name__, timeit.timeit("tf(f, k, x)", setup="from __main__ import f, tf, x, k", number=10)))
f1 deque([(99993, 99994, 99995), (99996, 99997, 99998), (99999,)], maxlen=3)
f2 deque([(99993, 99994, 99995), (99996, 99997, 99998), (99999,)], maxlen=3)
f3 deque([(99993, 99994, 99995), (99996, 99997, 99998), (99999,)], maxlen=3)
f4 deque([(99993, 99994, 99995), (99996, 99997, 99998), (99999,)], maxlen=3)
Case small, 10,000 x 3
f1 0.0125 secs
f2 0.0231 secs
f3 0.0185 secs
f4 0.0250 secs
Case med, 100,000 x 3
f1 0.1239 secs
f2 0.2270 secs
f3 0.1845 secs
f4 0.2477 secs
Case large, 1,000,000 x 3
f1 1.2140 secs
f2 2.2431 secs
f3 1.7967 secs
f4 2.4697 secs
list(more_itertools.chunked(range(11), 3))
# [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10]]
iter(functools.partial(more_itertools.take, n, iter(iterable)), [])