带 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 方法,如果对象由垃圾收集器管理,则会增加额外的垃圾收集器开销。"
我无法理解 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 方法,如果对象由垃圾收集器管理,则会增加额外的垃圾收集器开销。"