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