Długość wyjścia generatora [duplikat]
To pytanie ma już odpowiedź tutaj:
- uzyskanie liczby elementów w iteratorze w Pythonie 15 odpowiedzi
Python zapewnia miłą metodę uzyskiwania długości chętnego iterowalnego, len(x)
czyli. Ale nie mogłem znaleźć niczego podobnego dla leniwych iterabli reprezentowanych przez składanie generatorów i funkcje. Oczywiście nie jest trudno napisz coś w stylu:
def iterlen(x):
n = 0
try:
while True:
next(x)
n += 1
except StopIteration: pass
return n
Ale nie mogę pozbyć się uczucia, że zmieniam rower.
(kiedy pisałem funkcję, uderzyła mnie myśl: może naprawdę nie ma takiej funkcji, ponieważ "niszczy" jej argument. Nie jest to jednak problem dla mojej sprawy).
P. S.: jeśli chodzi o pierwsze odpowiedzi - tak, coś w rodzaju len(list(x))
też by zadziałało, ale to drastycznie zwiększa wykorzystanie pamięci.
9 answers
Nie ma takiego, ponieważ nie możesz tego zrobić w ogólnym przypadku - co jeśli masz leniwy nieskończony generator? Na przykład:
def fib():
a, b = 0, 1
while True:
a, b = b, a + b
yield a
To nigdy nie kończy się, ale wygeneruje liczby Fibonacciego. Możesz uzyskać dowolną liczbę Fibonacciego, dzwoniąc next()
.
Jeśli naprawdę potrzebujesz znać liczbę pozycji, nie możesz iterować ich liniowo jeden raz, więc po prostu użyj innej struktury danych, takiej jak zwykła lista.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-05 17:03:39
Najprostszym sposobem jest prawdopodobnie sum(1 for _ in gen)
gdzie gen jest Twoim generatorem.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-13 10:16:04
def count(iter):
return sum(1 for _ in iter)
Albo jeszcze lepiej:
def count(iter):
try:
return len(iter)
except TypeError:
return sum(1 for _ in iter)
Jeśli nie jest iteracyjna, rzuci TypeError
.
Lub, jeśli chcesz policzyć coś konkretnego w generatorze:
def count(iter, key=None):
if key:
if callable(key):
return sum(bool(key(x)) for x in iter)
return sum(x == key for x in iter)
try:
return len(iter)
except TypeError:
return sum(1 for _ in iter)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-14 03:48:49
Możesz użyć enumerate (), aby zapętlić wygenerowany strumień danych, a następnie zwrócić ostatnią liczbę -- liczbę pozycji.
Próbowałem użyć itertools.count () z itertools.izip () ale bez powodzenia. To jest najlepsza / Najkrótsza odpowiedź jaką wymyśliłem:#!/usr/bin/python
import itertools
def func():
for i in 'yummy beer':
yield i
def icount(ifunc):
size = -1 # for the case of an empty iterator
for size, _ in enumerate(ifunc()):
pass
return size + 1
print list(func())
print 'icount', icount(func)
# ['y', 'u', 'm', 'm', 'y', ' ', 'b', 'e', 'e', 'r']
# icount 10
Rozwiązanie Kamila Kisiela jest o wiele lepsze:
def count_iterable(i):
return sum(1 for e in i)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-12 15:03:23
Użyj reduce (function, iterable[, initializer]) dla wydajnego pamięci rozwiązania czysto funkcjonalnego:
>>> iter = "This string has 30 characters."
>>> reduce(lambda acc, e: acc + 1, iter, 0)
30
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-05 08:11:36
Z definicji, tylko podzbiór generatorów powróci po określonej liczbie argumentów (ma predefiniowaną Długość), a nawet wtedy, tylko podzbiór tych skończonych generatorów ma przewidywalny koniec (dostęp do generatora może mieć skutki uboczne, które mogłyby zatrzymać generator wcześniej).
Jeśli chcesz zaimplementować metody length dla swojego generatora, musisz najpierw zdefiniować, co uważasz za "Długość" (czy jest to całkowita liczba elementów? ilość pozostałych elementów?), następnie zapakuj swój generator w klasę. Oto przykład:
class MyFib(object):
"""
A class iterator that iterates through values of the
Fibonacci sequence, until, optionally, a maximum length is reached.
"""
def __init__(self, length):
self._length = length
self._i = 0
def __iter__(self):
a, b = 0, 1
while not self._length or self._i < self._length:
a, b = b, a + b
self._i += 1
yield a
def __len__(self):
"This method returns the total number of elements"
if self._length:
return self._length
else:
raise NotImplementedError("Infinite sequence has no length")
# or simply return None / 0 depending
# on implementation
Oto jak go używać:
In [151]: mf = MyFib(20)
In [152]: len(mf)
Out[152]: 20
In [153]: l = [n for n in mf]
In [154]: len(l)
Out[154]: 20
In [155]: l
Out[155]:
[1,
1,
2,
...
6765]
In [156]: mf0 = MyFib(0)
In [157]: len(mf0)
---------------------------------------------------------------------------
NotImplementedError Traceback (most recent call last)
<ipython-input-157-2e89b32ad3e4> in <module>()
----> 1 len(mf0)
/tmp/ipython_edit_TWcV1I.py in __len__(self)
22 return self._length
23 else:
---> 24 raise NotImplementedError
25 # or simply return None / 0 depending
26 # on implementation
NotImplementedError:
In [158]: g = iter(mf0)
In [159]: l0 = [g.next(), g.next(), g.next()]
In [160]: l0
Out[160]: [1, 1, 2]
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-28 19:51:29
Spróbuj more_itertools
pakiet dla prostego rozwiązania. Przykład:
>>> import more_itertools
>>> it = iter("abcde") # sample generator
>>> it
<str_iterator at 0x4ab3630>
>>> more_itertools.ilen(it)
5
Zobacz ten post dla innego przykładu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:54:41
To jest hack, ale jeśli naprawdę chcesz mieć len
pracę nad ogólnym iterable( zużywając go w sposób), możesz utworzyć własną wersję len
.
Funkcja len
jest zasadniczo równoważna następującym (choć implementacje zazwyczaj zapewniają pewne optymalizacje, aby uniknąć dodatkowego wyszukiwania):
def len(iterable):
return iterable.__len__()
Dlatego możemy zdefiniować nasze new_len
, aby spróbować tego, a jeśli __len__
nie istnieje, policz liczbę elementów samodzielnie, zużywając iterable:
def new_len(iterable):
try:
return iterable.__len__()
except AttributeError:
return sum(1 for _ in iterable)
The powyższe działa w Pythonie 2/3 i (o ile wiem) powinny obejmować każdy możliwy rodzaj iterable.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-11 23:35:35
Więc dla tych, którzy chcieliby poznać podsumowanie tej dyskusji. Ostateczne wyniki za zliczanie wyrażenia generatora o długości 50 milionów za pomocą:
-
len(list(gen))
, -
len([_ for _ in gen])
, -
sum(1 for _ in gen),
-
ilen(gen)
(from more_itertool), -
reduce(lambda c, i: c + 1, gen, 0)
,
Posortowane według wydajności wykonania (w tym zużycia pamięci), zaskoczy Cię:
```
1: test_list.py:8: 0.492 KiB
gen = (i for i in data*1000); t0 = monotonic(); len(list(gen))
('list, sec', 1.9684218849870376)
2: test_list_compr. py: 8: 0.867 KiB
gen = (i for i in data*1000); t0 = monotonic(); len([i for i in gen])
('list_compr, sec', 2.5885991149989422)
3: test_sum. py: 8: 0.859 KiB
gen = (i for i in data*1000); t0 = monotonic(); sum(1 for i in gen); t1 = monotonic()
('sum, sec', 3.441088170016883)
4: more_itertools / more. py: 413: 1.266 KiB
d = deque(enumerate(iterable, 1), maxlen=1)
test_ilen.py:10: 0.875 KiB
gen = (i for i in data*1000); t0 = monotonic(); ilen(gen)
('ilen, sec', 9.812256851990242)
5: test_reduce. py: 8: 0.859 KiB
gen = (i for i in data*1000); t0 = monotonic(); reduce(lambda counter, i: counter + 1, gen, 0)
('reduce, sec', 13.436614598002052) ```
Więc, len(list(gen))
jest najczęstsze i mniej zużywalne pamięci
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-19 09:29:24