Как len (генератор ()) [дубликат]
этот вопрос уже есть ответ здесь:
длина выхода генератора [дубликат]
9 ответов
генераторы Python очень полезны. Они имеют преимущества перед функциями, которые возвращают списки. Однако, вы могли бы len(list_returning_function()). Есть ли способ, чтобы len(generator_function())?
обновление:
Конечно len(list(generator_function())) будет работать.....
Я пытаюсь использовать генератор, который я создал внутри нового генератора, который я создаю. В рамках расчета в новом генераторе он должен знать длину старого. Однако я хотел бы сохранить оба из них вместе с теми же свойствами, что и генератор, в частности-не поддерживать весь список в памяти, как это может быть очень долго.
обновление 2:
Предположим, генератор знает ее длина, даже с первого шага. Кроме того, нет никаких причин поддерживать len() синтаксис. Пример-если функции в Python являются объектами, не могу ли я назначить длину переменной этого объекта, которая будет доступна для нового генератора?
8 ответов:
генераторы не имеют длины, они не являются коллекциями в конце концов.
генераторы функции с внутренним состоянием (и причудливый синтаксис). Вы можете многократно вызывать их, чтобы получить последовательность значений, поэтому вы можете использовать их в цикле. Но они не содержат никаких элементов, поэтому запрос длины генератора похож на запрос длины функции.
Если функции в Python являются объектами, не могу ли я присвоить длину a переменная этого объект, который будет доступен для нового генератора?
функции являются объектами, но вы не можете назначить им новые атрибуты. Причина, вероятно, в том, чтобы сохранить такой базовый объект как можно более эффективным.
однако вы можете просто вернуть
(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))но вы, вероятно, могли бы сделать что-то более эффективное, если вы действительно намерены отказаться от результатов.
можно использовать
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
вы можете объединить преимущества генераторов с уверенностью
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, но не создает явный список.
можно использовать
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находится в глобальном пространстве имен, поэтому импорт не требуется.
Comments