我无法理解yield
关键字
我理解程序执行时会发生什么影响,但我不太了解它使用了多少内存。
我会尝试用例子来解释我的怀疑 我们假设我们有三个功能:
HUGE_NUMBER = 9223372036854775807
def function1():
for i in range(0, HUGE_NUMBER):
yield i
def function2():
x = range(0, HUGE_NUMBER)
for i in x:
yield i
def function3(file):
with open(file, 'r') as f:
dictionary = dict(csv.reader(f, delimiter = ' '))
for k,v in dictionary.iteritems():
yield k,v
如果我迭代第一个函数返回的生成器,那么巨大的范围是否实际存储在内存中?
第二个功能怎么样?
如果我迭代第三个函数返回的生成器,那么我的程序是否会使用更少的内存(而不是仅仅创建该字典并直接迭代它)?
答案 0 :(得分:5)
Python 2 range()
函数生成的巨大列表需要存储,是的,并且会在生成器函数的整个生命周期内占用内存。
生成器函数可以内存有效,前提是它生成的结果是根据需要计算的,但range()
函数会预先生成所有结果。< / p>
你可以计算下一个数字:
def function1():
i = 0
while i < HUGE_NUMBER:
yield i
i += 1
并且您得到的结果相同,但您不会一次性存储整个范围内的所有数字。这基本上就是xrange()
object的循环;它根据要求计算数字。 (在Python 3中xrange()
替换了range()
)。
同样适用于您的function3
;您首先将整个文件读入字典,以便在迭代时仍然存储在内存中。之后不需要将整个文件读入内存中以生成每个元素。您可以循环遍历文件并生成行:
def function3(file):
seen = set()
with open(file, 'r') as f:
reader = csv.reader(f, delimiter = ' ')
for k, v in reader:
if k in seen:
# already seen
continue
seen.add(k)
yield k, v
这只存储了可以避免重复的密钥(就像字典一样),但是没有存储这些值。迭代生成器时内存会增加。如果重复项不是问题,您可以省略完全跟踪看到的键:
def function3(file):
with open(file, 'r') as f:
reader = csv.reader(f, delimiter = ' ')
for k, v in reader:
yield k, v
甚至
def function3(file):
with open(file, 'r') as f:
reader = csv.reader(f, delimiter = ' ')
return reader
毕竟读者是可迭代的。
答案 1 :(得分:0)
生成器对象包含对函数范围的引用,以及对其中所有本地对象的扩展。减少内存使用的方法是在每个级别使用迭代器,而不仅仅是在顶层。
答案 2 :(得分:-1)
如果要检查对象使用了多少内存,可以遵循this post作为代理。我发现它很有帮助。
“尝试一下:
sys.getsizeof(object)
getsizeof()调用对象的 sizeof 方法,如果该对象由垃圾收集器管理,则会添加额外的垃圾收集器开销。”