python怎么打印列表长度_关于python:生成器输出的长度

本问题已经有最佳答案,请猛点这里访问。

Python提供了一种很好的方法来获取渴望的可迭代len(x)的长度。 但是对于以生成器理解和函数表示的惰性可迭代对象,我找不到类似的东西。 当然,写这样的东西并不难:

1

2

3

4

5

6

7

8def iterlen(x):

n = 0

try:

while True:

next(x)

n += 1

except StopIteration: pass

return n

但是我无法摆脱正在重新装备自行车的感觉。

(虽然我正在键入该函数,但我的脑海中浮现了一个念头:也许确实没有这样的函数,因为它"破坏了"其参数。不过,对于我的情况而言,这不是问题)。

P.S .:关于第一个答案-是的,类似len(list(x))的东西也可以工作,但是会大大增加内存的使用。

P.P.S .:重新检查...忽略P.S.,似乎我在尝试时出错,它可以正常工作。 抱歉,添麻烦了。

建议仅将标题更改为发电机输出长度-可以扔掉重复的物品。 否则,这个问题就会与另一个混淆。

reimplementing a bicycle-几乎就像在重新发明轮子一样,只有程序员这样说。

最简单的方法可能只是sum(1 for _ in gen),其中gen是生成器。

尽管我喜欢此解决方案,但这里的主要缺点是,通过阅读您想要实现的代码,它一点都不明显。如果我在其他人的代码中看到这一行,我会停下来想一想"他为什么要在这里取这笔钱?" -除非之前没有看到这个" hack"。

@CharlesSalvia那是对恕我直言的评论。 Id说,获取生成器的长度值得评论。

另一个主要缺点是,它耗尽了发电机的功率只是为了达到长度,这通常首先破坏了发电机的整个目的。

请注意,这可能会减少内存消耗,但似乎比仅将其转换为列表要慢。

没有一个是因为您通常无法做到这一点-如果您有一个惰性无限生成器怎么办?例如:

1

2

3

4

5def fib():

a, b = 0, 1

while True:

a, b = b, a + b

yield a

这永远不会终止,但会生成斐波那契数。您可以通过调用next()获得任意数量的斐波纳契数。

如果您确实需要知道项目的数量,那么无论如何都无法一次线性地遍历它们,因此只能使用其他数据结构,例如常规列表。

我不确定我是否相信/接受这种解释。 sum采取可迭代方式,即使该可迭代方式可能是无限的,因此"在一般情况下您不能做到"比在一般情况下可以做到的要多。也许更可能的原因是人们"期望" len为O(1),这对于一般的可迭代项而言不是吗?

常规列表会占用更多内存,这是OP希望避免的事情。

@Steve Jessop:如果您有很多对象,则将它们计数通常显然是O(n)。如果在收集对象时跟踪对象的数量,则为O(1)。在许多特殊情况下,您可能可以使用对象的性质来构成更好的算法(即通过称量米数)。如果对象在内存中对齐,则可以使用内存消耗来计数对象。但是对于发电机,通常没有这种方法。

我有一个过滤后的列表,预计大约为2000000000个元素。我不能只使用常规列表;我需要使用发电机。现在,由于这些元素的来源方式,我实际上可以非常高效地运行它们-我无法存储它们,因为我没有40个内存。这个答案对我完全没有用。

1

2def count(iter):

return sum(1 for _ in iter)

或者更好:

1

2

3

4

5def count(iter):

try:

return len(iter)

except TypeError:

return sum(1 for _ in iter)

如果不是可迭代的,它将抛出一个TypeError。

或者,如果您想计算生成器中特定的内容:

1

2

3

4

5

6

7

8

9def count(iter, key=None):

if key:

if callable(key):

return sum(bool(key(x)) for x in iter)

return sum(x == key for x in iter)

try:

return len(iter)

except TypeError:

return sum(1 for _ in iter)

因此,对于那些想了解该讨论摘要的人。使用以下方法计算长度为5000万的生成器表达式的最终最高分:

len(list(gen))

len([_ for _ in gen]),

sum(1 for _ in gen),

ilen(gen)(来自more_itertool),

reduce(lambda c, i: c + 1, gen, 0),

按执行性能(包括内存消耗)排序,会让您感到惊讶:

```

1:test_list.py:8:0.492 KiB

1gen = (i for i in data*1000); t0 = monotonic(); len(list(gen))

("列表,秒",1.9684218849870376)

2:test_list_compr.py:8:0.867 KiB

1gen = (i for i in data*1000); t0 = monotonic(); len([i for i in gen])

('list_compr,sec',2.5885991149989422)

3:test_sum.py:8:0.859 KiB

1gen = (i for i in data*1000); t0 = monotonic(); sum(1 for i in gen); t1 = monotonic()

('sum,sec',3.441088170016883)

4:more_itertools / more.py:413:1.266 KiB

1

2

3

4d = deque(enumerate(iterable, 1), maxlen=1)

test_ilen.py:10: 0.875 KiB

gen = (i for i in data*1000); t0 = monotonic(); ilen(gen)

('ilen,sec',9.812256851990242)

5:test_reduce.py:8:0.859 KiB

1gen = (i for i in data*1000); t0 = monotonic(); reduce(lambda counter, i: counter + 1, gen, 0)

('reduce,sec',13.436614598002052)

```

因此,len(list(gen))是最频繁且消耗较少的内存

这是最好的答案

您可以使用enumerate()遍历生成的数据流,然后返回最后一个数字-项目数。

我尝试将itertools.count()与itertools.izip()一起使用,但没有运气。这是我提出的最好/最短的答案:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19#!/usr/bin/python

import itertools

def func():

for i in 'yummy beer':

yield i

def icount(ifunc):

size = -1 # for the case of an empty iterator

for size, _ in enumerate(ifunc()):

pass

return size + 1

print list(func())

print 'icount', icount(func)

# ['y', 'u', 'm', 'm', 'y', ' ', 'b', 'e', 'e', 'r']

# icount 10

Kamil Kisiel的解决方案更好:

1

2def count_iterable(i):

return sum(1 for e in i)

使用reduce(function,iterable [,initializer])获得内存有效的纯函数解决方案:

1

2

3>>> iter ="This string has 30 characters."

>>> reduce(lambda acc, e: acc + 1, iter, 0)

30

您的时间安排不对,因为迭代器正在被消耗。实际上,只有在len(list(iter))上的第一次尝试才对任何值进行迭代,所有其他值都在计数零长度序列。在我的测试中,reduce比len(list()),enumerate和sum慢。

@Blckknght谢谢,纠正了。

尝试使用more_itertools软件包以获得简单的解决方案。例:

1

2

3

4

5

6

7

8>>> import more_itertools

>>> it = iter("abcde") # sample generator

>>> it

>>> more_itertools.ilen(it)

5

有关另一个应用示例,请参见本文。

根据定义,只有一定数量的参数(具有预定义的长度)后,才会生成器的子集返回,即使这样,这些有限生成器中只有一个子集具有可预测的结局(访问生成器会产生副作用,即可以更早停止发电机)。

如果要为生成器实现length方法,则必须首先定义您认为的"长度"(是元素的总数还是剩余元素的数量?),然后将生成器包装在一个类中。这是一个例子:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25class MyFib(object):

"""

A class iterator that iterates through values of the

Fibonacci sequence, until, optionally, a maximum length is reached.

"""

def __init__(self, length):

self._length = length

self._i = 0

def __iter__(self):

a, b = 0, 1

while not self._length or self._i < self._length:

a, b = b, a + b

self._i += 1

yield a

def __len__(self):

"This method returns the total number of elements"

if self._length:

return self._length

else:

raise NotImplementedError("Infinite sequence has no length")

# or simply return None / 0 depending

# on implementation

使用方法如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42In [151]: mf = MyFib(20)

In [152]: len(mf)

Out[152]: 20

In [153]: l = [n for n in mf]

In [154]: len(l)

Out[154]: 20

In [155]: l

Out[155]:

[1,

1,

2,

...

6765]

In [156]: mf0 = MyFib(0)

In [157]: len(mf0)

---------------------------------------------------------------------------

NotImplementedError Traceback (most recent call last)

in ()

----> 1 len(mf0)

/tmp/ipython_edit_TWcV1I.py in __len__(self)

22 return self._length

23 else:

---> 24 raise NotImplementedError

25 # or simply return None / 0 depending

26 # on implementation

NotImplementedError:

In [158]: g = iter(mf0)

In [159]: l0 = [g.next(), g.next(), g.next()]

In [160]: l0

Out[160]: [1, 1, 2]

这是实现可以为len()函数提供长度的迭代器/生成器的解决方案。您可以通过实现自己的__iter__方法以及必要时自己的__init__和__len__方法来从此类派生生成器。这种模式可能很有用,例如对于某些ORM类型的对象,您可以在其中执行SQL查询,然后使用游标(通过迭代器)逐行获取结果,并且__len__方法从实际的SQL查询中获取计数。

这是一个技巧,但是如果您真的想让len在常规可迭代项上工作(以某种方式使用),则可以创建自己的len版本。

len函数在本质上等效于以下内容(尽管实现通常会提供一些优化以避免不必要的查找):

1

2def len(iterable):

return iterable.__len__()

因此,我们可以定义new_len进行尝试,如果__len__不存在,则可以通过消耗iterable来自己计算元素的数量:

1

2

3

4

5def new_len(iterable):

try:

return iterable.__len__()

except AttributeError:

return sum(1 for _ in iterable)

上面的代码在Python 2/3中有效,并且(据我所知)应该涵盖所有可能的迭代类型。

覆盖内置函数将掩盖原始行为,从而导致难以(或不可能)调试代码。您确实应该为该函数使用一个不同的名称,即不要命名为len ...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值