2015-01-06 3 views
2

Для следующего кода:размера объекта генератора в 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. Но ни один элемент не ссылается на второй цикл.

Почему объект-генератор принимает одну и ту же память, когда он был создан, и как это происходит при завершении итерации по нему?

ответ

5

Пространство, которое занимает генератор, находится в памяти просто бухгалтерская информация. В нем хранится ссылка на объект фрейма (администрирование для запуска кода Python, например, locals), или оно работает прямо сейчас, и ссылка на объект кода сохраняется. Ничего больше:

>>> 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-разрядная ОС X составляет 80 байт). sys.getsizeof() сообщает о размере только, что структура реализована в C, и она не рекурсирует по указателям.

Таким образом, этот объем памяти не изменится, когда вы пропустите генератор. Связанный кадр может изменяться в зависимости от того, сколько памяти используется (если выражение генератора ссылается на большие объекты на один конец или другой), но вы не увидите этого с результатом sys.getsizeof() на объекте генератора; смотреть на каркасных местными жителями вместо:

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

Объект .0 является range() итератора, что генератор использует в цикле for, i является целью по for петли.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 и, таким образом, могут быть глубоко изучены.

2

Генератор 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 и сколько памяти потребуется. Сравните, как быстро этот код работает.

Смежные вопросы