有没有内置的方法来获取python中迭代的长度?

时间:2008-12-24 05:55:38

标签: python iterator

例如,Python中的文件是可迭代的 - 它们遍历文件中的行。我想计算行数。

一种快速方法是:

lines = len(list(open(fname)))

但是,这会将整个文件加载到内存中(一次)。这相当违背了迭代器的目的(只需要将当前行保留在内存中)。

这不起作用:

lines = len(line for line in open(fname))

因为发电机没有长度。

有没有办法在定义计数函数时做到这一点?

def count(i):
    c = 0
    for el in i: c += 1
    return c
编辑:为了澄清,我知道必须阅读整个文件!我只是不想在内存中一次性=)。

10 个答案:

答案 0 :(得分:61)

没有遍历迭代并计算迭代次数,没有。这就是使它成为可迭代而不是列表的原因。这甚至不是特定于python的问题。查看经典的链表数据结构。查找长度是一个O(n)操作,涉及迭代整个列表以查找元素的数量。

如上所述,您可以将功能降低到:

def count_iterable(i):
    return sum(1 for e in i)

当然,如果您要定义自己的可迭代对象,则可以自己实现__len__并在某处保留元素数。

答案 1 :(得分:19)

如果您需要计算行数,我不知道有更好的方法:

line_count = sum(1 for line in open("yourfile.txt"))

答案 2 :(得分:10)

我已经使用了这个重新定义了一段时间了:

def len(thingy):
    try:
        return thingy.__len__()
    except AttributeError:
        return sum(1 for item in iter(thingy))

答案 3 :(得分:10)

cardinality包提供了一个有效的count()函数和一些相关函数来计算和检查任何可迭代的大小:http://cardinality.readthedocs.org/

import cardinality

it = some_iterable(...)
print(cardinality.count(it))

在内部,它使用enumerate()collections.deque()将所有实际的循环和计数逻辑移动到C级别,从而在Python中的for循环中获得相当大的加速。

答案 4 :(得分:8)

绝对不是,原因很简单,因为不能保证迭代是有限的。

考虑这个完全合法的生成器功能:

def forever():
    while True:
        yield "I will run forever"

尝试使用len([x for x in forever()])计算此功能的长度显然不起作用。

正如您所指出的,迭代器/生成器的大部分用途是能够处理大型数据集而无需将其全部加载到内存中。你不能立即获得长度的事实应该被视为权衡。

答案 5 :(得分:3)

事实证明,这个common problem已经实施了解决方案。考虑使用more_itertools中的ilen()函数。

more_itertools.ilen(iterable)

在文件中打印多行的示例(我们使用with语句来安全地处理关闭文件):

# Example
import more_itertools

with open("foo.py", "r+") as f:
    print(more_itertools.ilen(f))

# Output: 433

此示例返回与先前针对文件中的总计行显示的解决方案相同的结果:

# Equivalent code
with open("foo.py", "r+") as f:
    print(sum(1 for line in f))

# Output: 433

答案 6 :(得分:1)

由于当时显然没有发现重复,因此我还将在此发布my answer to the duplicate的摘录:

有一种方法可以在迭代次数可能较长的情况下比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)

len(list(it))ilen(it)在CPython上用C代码执行循环({dequecountzip都在C中实现);避免每个循环执行字节码通常是CPython性能的关键。

我不会在这里重复所有性能数字,而是将您指向my answer with the full perf details

答案 7 :(得分:0)

如果您考虑一下,我们如何建议您在不读取换行符的整个文件的情况下找到文件中的行数?当然,您可以找到文件的大小,如果您可以保证行的长度是x,则可以获得文件中的行数。但除非你有某种约束,否则我看不出它是如何起作用的。此外,由于iterables可以无限长......

答案 8 :(得分:0)

对于过滤,可以使用以下变体:

sum(is_good(item) for item in iterable)

它自然可以被理解为“计算好物品”,比以下内容更短,更简单(尽管习惯用法更少)

sum(1 for item in iterable if is_good(item)))

注意:在文档中指定了True在数字上下文中的值为1的事实 (https://docs.python.org/3.6/library/stdtypes.html#boolean-values),因此这种强制性不是hack(与C / C ++等其他语言相对)。

答案 9 :(得分:-1)

我在我的一些代码中的两个常见过程之间进行了测试,它找到了n个顶点上有多少个图,以查看生成列表的元素计数的哪个方法更快。 Sage有一个生成器图(n),它生成n个顶点上的所有图形。我创建了两个函数,它们以两种不同的方式获取迭代器获得的列表长度,并使用time.time()函数为每个函数计时(平均超过100次测试运行)。功能如下:

def test_code_list(n):
    l = graphs(n)
    return len(list(l))

def test_code_sum(n):
    S = sum(1 for _ in graphs(n))
    return S

现在我为每个方法计时

import time

t0 = time.time()
for i in range(100):
    test_code_list(5)
t1 = time.time()

avg_time = (t1-t0)/10

print 'average list method time = %s' % avg_time


t0 = time.time()
for i in range(100):
    test_code_sum(5)
t1 = time.time()

avg_time = (t1-t0)/100

print "average sum method time = %s" % avg_time

平均列表方法时间= 0.0391882109642

平均和方法时间= 0.0418473792076

因此,通过这种方式计算n = 5个顶点上的图形数量,列表方法稍快一些(尽管100次测试运行的样本量不是很大)。但是当我通过在n = 7个顶点上尝试图形来增加计算列表的长度时(即将图形(5)改为图形(7)),结果如下:

平均列表方法时间= 4.14753051996

平均和方法时间= 3.96504004002

在这种情况下,sum方法稍快一些。总而言之,这两种方法的速度大致相同,但差异可能取决于列表的长度(也可能只是我平均超过100次测试运行,这不是很高 - 将永远采取否则)。