为什么生成器表达式需要大量内存? - python

问题

假设我要为所有小于n**2的数字查找20000000

我测试的所有三个变体的常规设置:

import time, psutil, gc

gc.collect()
mem_before = psutil.virtual_memory()[3]
time1 = time.time()

# (comprehension, generator, function)-code comes here

time2 = time.time()
mem_after =  psutil.virtual_memory()[3]

print "Used Mem = ", (mem_after - mem_before)/(1024**2)  # convert Byte to Megabyte
print "Calculation time = ", time2 - time1

计算这些数字的三个选项:

1.创建“通过”理解列表:

x = [i**2 for i in range(20000000)]

这确实很慢而且很耗时:

Used Mem =  1270  # Megabytes
Calculation time =  33.9309999943  # Seconds

2.使用'()'创建生成器:

x = (i**2 for i in range(20000000))

它比选项1快得多,但仍占用大量内存:

Used Mem =  611 
Calculation time =  0.278000116348 

3.定义生成器功能(最有效):

def f(n):
    i = 0
    while i < n:
        yield i**2
        i += 1
x = f(20000000)

它的消耗:

Used Mem =  0
Calculation time =  0.0

问题是:

  • 第一和第二个解决方案有什么区别?使用()会创建一个生成器,为什么它需要很多内存?
  • 是否有与我的第三个选项等效的内置函数?
  • 参考方案

  • 正如其他人在评论中指出的那样,range在Python 2中创建了list。因此,不是生成器本身占用了内存,而是生成器使用的range:
    x = (i**2 for i in range(20000000))  
    # builds a 2*10**7 element list, not for the squares , but for the bases
    
    >>> sys.getsizeof(range(100))
    872
    >>> sys.getsizeof(xrange(100))
    40
    >>> sys.getsizeof(range(1000))
    8720
    >>> sys.getsizeof(xrange(1000))
    40
    >>> sys.getsizeof(range(20000000))
    160000072
    >>> sys.getsizeof(xrange(20000000))
    40
    

    这也解释了为什么第二个版本(生成器表达式)使用第一个版本(列表推导)的大约一半的内存,因为第一个版本生成两个列表(用于底数和平方),而第二个版本仅生成一个列表基地。

  • xrange(20000000)可以大大提高内存使用率,因为它返回了惰性迭代。从本质上讲,这是一种内置的内存有效方式,可以在与您的第三个版本相对应的数字范围内进行迭代(具有startstopstep的更多灵活性):
    x = (i**2 for i in xrange(20000000))
    

    在Python 3中,range本质上就是Python 2中的xrange
    但是,Python 3 range对象具有Python 2的xrange不具备的一些不错的功能,例如O(1)切片,包含等。

  • 一些参考:

  • Python2 xrange docs
  • Python3 range docs
  • Stack Overflow - "Should you always favor xrange() over range()?"
  • Martijn Pieters excellent answer to "Why is 1000000000000000 in range(1000000000000001) so fast in Python 3?"
  • Python:对于长时间运行的进程,通过还是休眠? - python

    我正在编写一个队列处理应用程序,该应用程序使用线程等待和响应要发送到该应用程序的队列消息。对于应用程序的主要部分,只需要保持活动状态即可。对于像这样的代码示例:而True: 通过要么而True: time.sleep(1)哪一个对系统的影响最小?除了保持python应用运行外,什么都不做的首选方式是什么? 参考方案 我可以想象time.sleep()会减少系…

    Python:无符号32位按位算术 - python

    试图回答另一篇有关其解决方案涉及IP地址和网络掩码的文章时,我陷入了普通的按位算法。在Python中,是否存在一种标准的方式来进行按位AND,OR,XOR,NOT运算,假设输入是“32位”(可能是负数)整数或long,并且结果必须是[[ 0,2 ** 32]?换句话说,我需要一个与无符号长整数之间的C按位运算有效的Python对应物。编辑:具体问题是这样的:…

    >> Python中的运算符 - python

    >>运算符做什么?例如,以下操作10 >> 1 = 5有什么作用? 参考方案 它是右移运算符,将所有位“右移”一次。二进制10是1010移到右边变成0101这是5

    Python:如何从字节中提取特定位? - python

    我有一条消息,显示为14 09 00 79 3d 00 23 27。我可以通过调用message[4]从此消息中提取每个字节,例如,这将给我3d。如何从该字节中提取单个8位?例如,如何将24-27位作为单个消息?只需28位? 参考方案 要回答问题的第二部分,您可以使用按位运算来获取特定的位值# getting your message as int i = …

    Python GPU资源利用 - python

    我有一个Python脚本在某些深度学习模型上运行推理。有什么办法可以找出GPU资源的利用率水平?例如,使用着色器,float16乘法器等。我似乎在网上找不到太多有关这些GPU资源的文档。谢谢! 参考方案 您可以尝试在像Renderdoc这样的GPU分析器中运行pyxthon应用程序。它将分析您的跑步情况。您将能够获得有关已使用资源,已用缓冲区,不同渲染状态上…