带 yield 的函数使用多少内存?

How much memory does a function with yield use?

我无法理解 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

如果我遍历第一个函数返回的生成器,这个巨大的范围是否真的存储在内存中?

第二个功能呢?

如果我对第三个函数返回的生成器进行迭代(而不是仅仅创建该字典并直接对其进行迭代),我的程序是否会使用更少的内存?

生成器对象包含对函数作用域的引用,并扩展为所有 个局部对象。减少内存使用的方法是在每个可能的级别使用迭代器,而不仅仅是在顶层。

Python 2 range() 函数生成的巨大列表需要存储,是的,并且会在生成器函数的整个生命周期内占用内存。

生成器函数 可以 内存高效,前提是它产生的结果是根据需要计算的,但是 range() 函数会产生所有结果 up前面.

您可以计算下一个数字:

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

毕竟 reader 是可迭代的。

如果你想查看一个对象使用了多少内存,你可以按照this post作为代理。我发现它很有帮助。

“试试这个:

sys.getsizeof(object)

getsizeof() 调用对象的 sizeof 方法,如果对象由垃圾收集器管理,则会增加额外的垃圾收集器开销。"

A recursive recipe