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

时间:2015-01-03 13:12:05

标签: python generator

任何人都可以解释这些示例中的生成器是如何工作的吗?

在此示例中来自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(无,1)[1] 和int(x)分开 - 这样做是否有优势?
  3. 在此示例中(x * x表示范围内的x(1,100000000))

    1. 当解释器评估此表达式时,Python 2中是否计算了范围(1,100000000)?
    2. 在此声明或首次运行发生器期间是否会发生这种情况
    3. 这在Python 3中有什么不同吗?
    4. 原因我有这个疑问就是这个片段:

      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.
      

      使用生成器超过列表或场景更有实际可行吗?

2 个答案:

答案 0 :(得分:5)

  1. 生成器是懒惰的,所以在迭代它们之前没有任何反应(例如,使用sum)。
  2. 它只是让它更具可读性。你可以这样写,如果你想: bytes = (int(x) for x in (line.rsplit(None,1)[1] for line in wwwlog) if x != '-')
  3. 是的,在Python 2 range中返回一个列表,您需要xrange来生成类似生成器的对象。
  4. 计算迭代生成器的时间,直到那时才进行任何操作。
  5. 是的,在Python 3 range中表现得像Python 2 xrange。同样的事情发生在mapfilter等其他功能上。
  6. 生成器的主要优点是它们不会立即存储整个内容。它们的主要缺点是你只能迭代它们一次。

答案 1 :(得分:0)

1.当我们连接这样的生成器时,在我们执行sum()之前是否会发生任何实际工作?

  

生成器表达式返回一个按需生成结果的对象,而不是构建一个   结果列表,所以你的答案是否定的(但仍取决于你对实际工作的意思)。

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

  

它只是让你的代码更具可读性并增加灵活性(基于你对该表达式的处理),你可以在一行中完成:

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

在此示例中(x * x表示范围内的x(1,100000000))

3.在Python 2中是否计算了范围(1,100000000)?

  

是的,它是剂量,而在python 2中你可以使用返回迭代器的xrange

4.如果是在本声明中或在发电机首次运行期间

  

...

5.在Python 3中这有什么不同吗?

  

在python 3中,内置函数返回一个迭代器,如openrange

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

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

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

>>> [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

事实上,至少在函数的基础上,编码列表理解基本上是相同的 在列表内置调用中包装生成器表达式以强制它生成所有它 立即生成一个列表:

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

列表理解等价

然而,在操作上,生成器表达式是非常不同的 - 而不是构建 结果列表在内存中,它们返回一个生成器对象,后者又支持 迭代协议在任何迭代上下文中一次产生一个结果列表:

>>> 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

我们通常不会看到发电机引擎盖下的下一个迭代器机器 像这样的压力,因为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和 排序的内置函数;列表理解;和我们学到的其他迭代上下文 关于第14章,例如any,all和list内置函数。 请注意,如果它们是生成器表达式,则不需要括号 括在其他括号中的唯一项目,如函数调用。额外的 - 但是,在第二次调用sort:

时需要这些论文
>>> 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]

生成器表达式主要是内存空间优化 - 它们不会重新 quire整个结果列表一次全部构建,作为方括号列表 理解确实如此。在实践中它们也可能运行得稍慢,所以它们可能也是如此 最好只用于非常大的结果集。关于per-的更权威的陈述 但是,formance必须等待我们将在本章后面编写的时序脚本。