簡體   English   中英

python中生成器對象的大小

[英]size of generator object in python

對於以下代碼:

import sys
x=(i for i in range(1,11))
print x


print 'Before starting iterating generator size is' ,sys.getsizeof(x)

print 'For first time'
for i in x:
    print i

print 'For second time , does not print anything'    
for i in x:
    print i # does not print anything

print 'After iterating generator size is' ,sys.getsizeof(x)

輸出是:

<generator object <genexpr> at 0x014C1A80>
Before starting iterating generator size is 40
For first time
1
2
3
4
5
6
7
8
9
10
For second time
After iterating generator size is 40

生成器對象的大小最初是40,當我完成迭代時它仍然是40.但是沒有元素從第二個循環引用。

為什么生成器對象在創建時會占用相同的內存,並且在完成迭代時會占用相同的內存?

發電機在內存中占用的空間只是簿記信息 其中保留了對框架對象的引用(管理正在運行的Python代碼,例如本地代碼),現在它正在運行,並保留對代碼對象的引用。 而已:

>>> x=(i for i in range(1,11))
>>> dir(x)
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__iter__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'close', 'gi_code', 'gi_frame', 'gi_running', 'next', 'send', 'throw']
>>> x.gi_frame
<frame object at 0x1053b4ad0>
>>> x.gi_running
0
>>> x.gi_code
<code object <genexpr> at 0x1051af5b0, file "<stdin>", line 1>

這只是3個引用,加上通常的Python對象類型信息(思考引用計數)和弱引用列表; 這是大約4個指針,一個整數和一個結構,在你的系統上占用40個字節(在我的系統上,64位OS X,它是80個字節)。 sys.getsizeof()只是結構的大小作為報告用C實現的,它不遞歸超過指針。

因此,當您運行生成器時,該內存量不會更改。 引用的幀可能會改變使用的內存量(如果生成器表達式引用大對象朝向一端或另一端)但是您不會在生成器對象上看到sys.getsizeof()的結果; 改為查看框架本地:

>>> next(x)
1
>>> x.gi_frame.f_locals
{'i': 1, '.0': <listiterator object at 0x105339dd0>}

.0對象是生成器在for循環中使用的range()迭代器, ifor循環目標。 listiterator是另一個可迭代對象,它具有對列表range()的私有引用以及位置計數器,因此每次請求它時它都可以產生下一個元素。

您無法查詢生成器的元素大小 ; 無論如何,它們都會根據需要生成元素,你不能事先“知道”它們會產生多少,也不能知道它們在運行后產生了多少。 sys.getsizeof()肯定不會告訴你; 它無論如何都是一種測量內存占用的工具,如果你想知道占用空間,你必須遞歸測量所有引用的對象。

可以看到發電機已完成從框架的運行; 一旦完成就會被清除

>>> x.gi_frame
<frame object at 0x1053b4ad0>
>>> list(x)
[2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> x.gi_frame is None
True

所以最后,用於生成器的內存駐留在幀中的結構中(本地,可能是全局,這些名稱空間中的每個對象可能再次引用其他對象),並且當生成器完成時,幀被清除並且生成器.gi_frame指針被更改為指向None單例,如果引用計數已降至0,則將幀清除。

所有這些只適用於發電機 ,而不適用於一般的迭代; 生成器是Python代碼,因此可以深入反思。

生成器x基本上是一個函數,無論何時調用它都將提供i的下一個值。 它不會提前計算所有值。 它等待它被調用,然后它計算並提供下一個值。

因此每次調用都會產生下一個值。

為什么x的大小不變? 嗯,這是因為x不是數字列表。 在過程的開始和結束時,它仍然是相同的功能。

這是使用發電機的優勢。 您不必在開始時將所有內容加載到內存中(因此它可以節省內存),並且(如果正確完成)您不必在實際需要之前計算任何內容(因此,如果某些值可以節省計算時間)不需要)。

看到這個:

x = (i for i in xrange(10**10))
for i in x:
    print i
    if i>10:
        break
print 'intermission'
for i in x:
    print i
    if i>20:
        break

(注意xrange ,而不是range ---使用range會導致提前計算發生)。 想象一下,實際生成從0到10**10的整數以及需要多少內存需要多長時間。 比較此代碼的運行速度。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM