列表上的生成器与链接生成器和内存消耗?

Generators on a list vs chaining generators and memory consumption?

谁能解释一下生成器在这些示例中的工作原理?

本例来自 http://www.dabeaz.com/generators/index.html

wwwlog = open("access-log")
bytecolumn = (line.rsplit(None,1)[1] for line in wwwlog)
bytes = (int(x) for x in bytecolumn if x != '-')
print "Total", sum(bytes)
  1. 当我们像这样链接生成器时,除了代码对象创建之外,在我们执行 sum() 之前是否会发生任何实际工作?
  2. 为什么我们需要做 line.split(None,1)[1] 和 int(x) 分开 - 这样做有优势吗?

在这个例子中(x*x for x in range(1,100000000))

  1. 当解释器计算这个表达式时,Python 2 中是否计算了范围(1,100000000)?
  2. 在该语句期间或在生成器的第一次运行期间是否发生这种情况
  3. 这在 Python 3 中有什么不同吗?

原因我怀疑是这个片段:

def foo():
    for each in range(1,100000):
      yield each

a = foo()

# Here range is not evaluated until generator is run or just 
# before first yield is hit which is expected.

a=(x for x in range(1,100000)) 

# I thought also does exact thing as that function and it i is a 
# syntactic sugar for a=foo() which also yields a generator object.

使用生成器比使用列表或更实用的场景有什么优势吗?

1.When 我们像这样链接生成器在我们执行 sum() 之前会发生任何实际工作吗?

Generator expression return an object that produces results on demand instead of building a result list so your answer is No (but it still depends on your mean about real work).

2.Why 我们是否需要分别执行 line.split(None,1)[1]int(x) - 这样做有优势吗?

it just make your code more readable and increase flexibility (based on what you may do with that expression), you can do it in one line :

(int(line.rsplit(None,1)[1]) for line in wwwlog if ine.rsplit(None,1)[1] != '-') 

在这个例子中(x*x for x in range(1,100000000))

3.Is range(1,100000000) 在 Python 2 ?

中计算

yes it dose , instead in python 2 you can use xrange that return an iterator.

4.If 在此语句期间或生成器的第一个 运行 期间也是如此

...?

5.Is 这个不同 Python 3 ?

in python 3 meany of built-in functions return an iterator like open or range

对于您的第 4 个问题和您在评论中的问题的回答,我认为 learning python by Mark Lutz 的这一部分可能会有所帮助:

生成器表达式:迭代器满足理解

在 Python 的所有最新版本中,迭代器和列表理解的概念是 结合了语言的新特性,生成器表达式。从句法上讲,gen- 运算符表达式就像普通的列表推导式一样,但它们包含在 括号而不是方括号:

>>> [x ** 2 for x in range(4)]
[0, 1, 4, 9] # List comprehension: build a list
>>> (x ** 2 for x in range(4))
<generator object at 0x011DC648> # Generator expression: make an iterable

事实上,至少在功能基础上,编码列表理解本质上是相同的 将生成器表达式包装在列表 built-in 调用中以强制它生成所有 立即生成列表:

>>> list(x ** 2 for x in range(4))
[0, 1, 4, 9]

列表理解等价

然而,在操作上,生成器表达式非常不同——而不是构建 内存中的结果列表,它们 return 一个生成器 object,它反过来支持 在任何迭代上下文中一次产生一个结果列表的迭代协议:

>>> G = (x ** 2 for x in range(4))
>>> next(G)
0
>>> next(G)
1
>>> next(G)
4
>>> next(G)
9
>>> next(G)
Traceback (most recent call last):
...more text omitted...
StopIteration

我们通常不会在生成器 ex- 的引擎盖下看到下一个迭代器机制 像这样压缩是因为 for 循环会自动为我们触发它:

>>> for num in (x ** 2 for x in range(4)):
...
print('%s, %s' % (num, num / 2.0))
...
0, 0.0
1, 0.5
4, 2.0
9, 4.5

正如我们已经了解到的,每个迭代上下文都会这样做,包括 sum 、 map 和 排序 built-in 函数;列出理解;以及我们学到的其他迭代上下文 关于第 14 章中的 any 、 all 和 list built-in 函数。 请注意,生成器表达式周围不需要括号,如果它们是 包含在其他括号中的唯一项目,就像函数调用的那些一样。额外的父母- 然而,在第二次调用 sorted 时需要这些:

>>> sum(x ** 2 for x in range(4))
14
>>> sorted(x ** 2 for x in range(4))
[0, 1, 4, 9]
>>> sorted((x ** 2 for x in range(4)), reverse=True)
[9, 4, 1, 0]
>>> import math
>>> list( map(math.sqrt, (x ** 2 for x in range(4))) )
[0.0, 1.0, 2.0, 3.0]

生成器表达式主要是 memory-space 优化——它们不会重新 要求一次性构建整个结果列表,如 square-bracketed 列表 理解力呢。他们在实践中也可能 运行 稍微慢一点,所以他们可能是 最好只用于非常大的结果集。比较权威的说法是per- 但是,性能必须等待我们将在本章稍后编写的计时脚本。

  1. 生成器是惰性的,因此在您遍历它们之前什么也不会发生(例如使用 sum)。
  2. 它只是让它更具可读性。如果你想,你可以这样写: bytes = (int(x) for x in (line.rsplit(None,1)[1] for line in wwwlog) if x != '-')
  3. 是的,在Python 2 range returns 一个列表中,你需要xrange 来生成类似生成器的对象。
  4. 迭代生成器时计算,在此之前什么都不做。
  5. 是的,在 Python 3 range 中表现得像 Python 2 的 xrange。同样的事情也发生在其他函数上,比如 mapfilter.

生成器的主要优点是它们不会一次存储全部内容。它们的主要缺点是您只能迭代一次。