Объекты генератора Python: __sizeof __()

Это может быть глупый вопрос, но я все равно спрошу. У меня есть объект-генератор:

>>> def gen():
...     for i in range(10):
...         yield i
...         
>>> obj=gen()

Я могу измерить его размер:

>>> obj.__sizeof__()
24

Говорят, что генераторы потребляются:

>>> for i in obj:
...     print i
...     
0
1
2
3
4
5
6
7
8
9
>>> obj.__sizeof__()
24

... но obj.__sizeof__() остается неизменным.

С помощью строк он работает так, как я ожидал:

>>> 'longstring'.__sizeof__()
34
>>> 'str'.__sizeof__()
27

Я был бы признателен, если бы кто-нибудь мог просветить меня.

Ответ 1

__sizeof__() не делает то, что вы думаете. Метод возвращает внутренний размер в байтах для данного объекта, а не количество элементов, которые будет возвращать генератор.

Python не может заранее знать размер генератора. Возьмем, к примеру, следующий бесконечный генератор (например, есть лучшие способы создания счетчика):

def count():
    count = 0
    while True:
        yield count
        count += 1

Этот генератор бесконечен; для него нет назначаемого размера. Однако сам объект-генератор принимает память:

>>> count.__sizeof__()
88

Обычно вы не вызываете __sizeof__(), вы оставляете это для функции sys.getsizeof(), которая также добавляет служебные данные сборщика мусора.

Если вы знаете, что генератор будет конечным, и вы должны знать, сколько элементов он возвращает, используйте:

sum(1 for item in generator)

но обратите внимание, что это истощает генератор.

Ответ 2

Как сказано в других ответах, __sizeof__ возвращает другую вещь.

Только некоторые итераторы имеют методы, возвращающие количество не возвращенных элементов. Например, listiterator имеет соответствующий метод __length_hint__:

>>> L = [1,2,3,4,5]
>>> it = iter(L)
>>> it
<listiterator object at 0x00E65350>
>>> it.__length_hint__()
5
>>> help(it.__length_hint__)
Help on built-in function __length_hint__:

__length_hint__(...)
    Private method returning an estimate of len(list(it)).

>>> it.next()
1
>>> it.__length_hint__()
4

Ответ 3

__sizeof__ возвращает размер памяти объекта в байтах, а не длину генератора, который невозможно определить спереди, так как генераторы могут расти неограниченно.

Ответ 4

Если вы уверены, что созданный генератор "конечен" (имеет счетное количество элементов), и вы не возражаете ждать, пока вы сможете использовать следующее, чтобы получить то, что вы хотите:

len(list(gen()))

Как утверждают другие плакаты __sizeof__(), это мера того, сколько памяти что-то занимает (концепция гораздо более низкого уровня, которая вам, вероятно, понадобится), а не ее длина (что не является особенностью генераторов, поскольку нет гарантии они имеют счетную длину).