Как len (generator ())

Генераторы Python очень полезны. Они имеют преимущества перед функциями, которые возвращают списки. Однако вы можете len(list_returning_function()) . Есть ли способ len(generator_function()) ?

ОБНОВИТЬ:
Конечно, len(list(generator_function())) будет работать …..
Я пытаюсь использовать генератор, который я создал внутри нового генератора, который я создаю. В рамках расчета в новом генераторе необходимо знать длину старого. Однако я хотел бы сохранить их вместе с теми же свойствами, что и генератор, в частности – не поддерживать весь список в памяти, поскольку это может быть очень долго.

ОБНОВЛЕНИЕ 2:
Предположим, что генератор знает, что это длина цели даже с первого шага. Кроме того, нет причин поддерживать синтаксис len() . Пример. Если функции в Python являются объектами, не могу ли я назначить длину переменной этого объекта, которая будет доступна для нового генератора?

8 Solutions collect form web for “Как len (generator ())”

Генераторы не имеют длины, они ведь не коллекции.

Генераторы – это функции с внутренним состоянием (и причудливым синтаксисом). Вы можете повторно вызвать их, чтобы получить последовательность значений, чтобы вы могли использовать их в цикле. Но они не содержат никаких элементов, поэтому запрос длины генератора похож на запрос длины функции.

если функции в Python являются объектами, не могу ли я назначить длину переменной этого объекта, которая будет доступна для нового генератора?

Функции – это объекты, но вы не можете назначать им новые атрибуты. Вероятно, причина заключается в том, чтобы такой базовый объект был максимально эффективным.

Однако вы можете просто вернуть пары (generator, length) из своих функций или обернуть генератор простым простейшим объектом:

 class GeneratorLen(object): def __init__(self, gen, length): self.gen = gen self.length = length def __len__(self): return self.length def __iter__(self): return self.gen g = some_generator() h = GeneratorLen(g, 1) print len(h), list(h) 

Преобразование в list , предложенный в других ответах, является наилучшим способом, если вы все еще хотите обработать элементы генератора впоследствии, но имеет один недостаток: он использует O (n) память. Вы можете подсчитать элементы в генераторе, не используя столько памяти:

 sum(1 for x in generator) 

Разумеется, имейте в виду, что это может быть медленнее, чем len(list(generator)) в общих реализациях Python, и если генераторы достаточно длинны для сложности памяти, операция займет довольно много времени. Тем не менее, я лично предпочитаю это решение, поскольку оно описывает то, что я хочу получить, и это не дает мне ничего лишнего, что не требуется (например, список всех элементов).

Также слушайте советы дельнана: если вы отбрасываете вывод генератора, очень вероятно, что есть способ вычислить количество элементов без его запуска или путем подсчета их другим способом.

Предположим, что у нас есть генератор:

 def gen(): for i in range(10): yield i 

Мы можем обернуть генератор вместе с известной длиной в объекте:

 import itertools class LenGen(object): def __init__(self,gen,length): self.gen=gen self.length=length def __call__(self): return itertools.islice(self.gen(),self.length) def __len__(self): return self.length lgen=LenGen(gen,10) 

Экземпляры LenGen сами являются генераторами, так как их вызов возвращает итератор.

Теперь мы можем использовать генератор lgen вместо gen и получить доступ len(lgen) :

 def new_gen(): for i in lgen(): yield float(i)/len(lgen) for i in new_gen(): print(i) 

Вы можете использовать len(list(generator_function()) , но это потребляет генератор, но это единственный способ узнать, сколько элементов сгенерировано. Поэтому вы можете захотеть сохранить список где-нибудь, если вы также хотите использовать Предметы.

 a = list(generator_function()) print(len(a)) print(a[0]) 

Вы можете len(list(generator)) но вы, вероятно, могли бы сделать что-то более эффективное, если вы действительно собираетесь отказаться от результатов.

Вы можете использовать reduce .

Для Python 3:

 >>> import functools >>> def gen(): ... yield 1 ... yield 2 ... yield 3 ... >>> functools.reduce(lambda x,y: x + 1, gen(), 0) 

В Python 2 reduce находится в глобальном пространстве имен, поэтому импорт не нужен.

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

 class MyIterable(object): def __init__(self, n): self.n = n def __len__(self): return self.n def __iter__(self): self._gen = self._generator() return self def _generator(self): # Put your generator code here i = 0 while i < self.n: yield i i += 1 def next(self): return next(self._gen) mi = MyIterable(100) print len(mi) for i in mi: print i, 

Это в основном простая реализация xrange , которая возвращает объект, который вы можете взять len, но не создает явный список.

Вы можете использовать send в качестве взлома:

 def counter(): length = 10 i = 0 while i < length: val = (yield i) if val == 'length': yield length i += 1 it = counter() print(it.next()) #0 print(it.next()) #1 print(it.send('length')) #10 print(it.next()) #2 print(it.next()) #3 
  • Самый pythonic способ разрезать список Python каждые 100 элементов
  • передача информации о настройках для удаления из носа
  • Использование урожая с пониманием dict
  • Как получить имя пользователя Windows для посетителя страницы с помощью Flask?
  • Считываемый эквивалент C # операции кусочка Python
  • Различные виды входа в Pyramid
  • Перечисление всех путей в дереве
  • Проектирование фильтра и извлечение частоты в Python
  • Модуль python, импортированный извне virtualenv
  • Сделать поток ждать либо таймера, либо сигнала?
  • шаблон не найден, развертывание Pyramid на Webfaction
  • Python - лучший язык программирования в мире.