Fre*_*Foo 60 python iterator iterable generator
如果我想要迭代中的项目数量而不关心元素本身,那么获得该元素的pythonic方法是什么?现在,我会定义
def ilen(it):
return sum(itertools.imap(lambda _: 1, it)) # or just map in Python 3
Run Code Online (Sandbox Code Playgroud)
但我明白lambda接近被认为是有害的,lambda _: 1当然不是很漂亮.
(这个用例是计算匹配正则表达式的文本文件中的行数,即grep -c.)
Sve*_*ach 137
通常的方式是
sum(1 for i in it)
Run Code Online (Sandbox Code Playgroud)
Sha*_*ger 30
有效的方法比sum(1 for i in it)迭代可能很长(并且当可迭代时间短时没有意义地慢),同时保持固定的内存开销行为(不同len(list(it))),以避免交换抖动和更大输入的重新分配开销:
# On Python 2 only, get zip that lazily generates results instead of returning list
from future_builtins import zip
from collections import deque
from itertools import count
def ilen(it):
# Make a stateful counting iterator
cnt = count()
# zip it with the input iterator, then drain until input exhausted at C level
deque(zip(it, cnt), 0) # cnt must be second zip arg to avoid advancing too far
# Since count 0 based, the next value is the count
return next(cnt)
Run Code Online (Sandbox Code Playgroud)
像len(list(it))它执行上CPython的C代码环路(deque,count和zip在C中的所有实现的); 避免每个循环执行字节代码通常是CPython中性能的关键.
令人惊讶地难以提出用于比较性能的公平测试用例(list使用__length_hint__哪些不可用于任意输入迭代的作弊itertools,不提供的功能__length_hint__通常具有在每个循环返回的值时更快地工作的特殊操作模式在请求下一个值之前释放释放deque,maxlen=0将执行此操作).我使用的测试用例是创建一个生成器函数,该函数将接受输入并返回缺少特殊itertools返回容器优化的C级生成器,或__length_hint__使用Python 3.3 yield from:
def no_opt_iter(it):
yield from it
Run Code Online (Sandbox Code Playgroud)
然后使用ipython %timeit魔法(用不同的常数代替100):
>>> %%timeit -r5 fakeinput = (0,) * 100
... ilen(no_opt_iter(fakeinput))
Run Code Online (Sandbox Code Playgroud)
当输入不够大len(list(it))而导致内存问题时,在运行Python 3.5 x64的Linux机器上def ilen(it): return len(list(it)),无论输入长度如何,我的解决方案都需要大约50%的时间.
对于最小的输入,调用deque/ zip/ count/ 的设置成本next意味着它比这样的时间长得多def ilen(it): sum(1 for x in it)(对于长度为0的输入,我的机器上大约200 ns,比简单的sum方法增加了33%),但对于输入时间越长,每个附加元素的运行时间约为一半; 对于长度为5的输入,成本是等价的,并且在50-100范围内的某处,与实际工作相比,初始开销是不明显的; 这种sum方法大约需要两倍的时间.
基本上,如果内存使用很重要或输入没有有限的大小而你关心速度而不是简洁,请使用此解决方案.如果输入是有界的和小的,len(list(it))可能是最好的,如果它们是无界限的,但简单/简洁是重要的,你可以使用sum(1 for x in it).
一个简短的方法是:
def ilen(it):
return len(list(it))
Run Code Online (Sandbox Code Playgroud)
请注意,如果要生成大量元素(例如,数万或更多),则将它们放在列表中可能会成为性能问题.然而,这是一个简单的表达方式,在大多数情况下性能不重要.
more_itertools是实现ilen工具的第三方库。 pip install more_itertools
import more_itertools as mit
mit.ilen(x for x in range(10))
# 10
Run Code Online (Sandbox Code Playgroud)
len(list(it))
Run Code Online (Sandbox Code Playgroud)
不过,如果它是无限生成器,它可能会挂起。
| 归档时间: |
|
| 查看次数: |
27133 次 |
| 最近记录: |