Zbuduj podstawowy Iterator Pythona
Jak stworzyć funkcję iteracyjną (lub obiekt iteracyjny) w Pythonie?
9 answers
Obiekty iteratora w Pythonie są zgodne z protokołem iteratora, co oznacza, że dostarczają dwie metody: __iter__()
i next()
. __iter__
zwraca obiekt iterator i jest niejawnie wywoływany na początku pętli. Metoda next()
zwraca następną wartość i jest domyślnie wywoływana przy każdym przyroście pętli. next()
podnosi wyjątek Stopiteracji, gdy nie ma więcej wartości do zwrócenia, który jest domyślnie przechwytywany przez konstrukcje pętlowe, aby zatrzymać iterację.
Oto prosty przykład z licznika:
class Counter:
def __init__(self, low, high):
self.current = low
self.high = high
def __iter__(self):
return self
def next(self): # Python 3: def __next__(self)
if self.current > self.high:
raise StopIteration
else:
self.current += 1
return self.current - 1
for c in Counter(3, 8):
print c
To wydrukuje:
3
4
5
6
7
8
Jest to łatwiejsze do napisania za pomocą generatora, co zostało omówione w poprzedniej odpowiedzi:
def counter(low, high):
current = low
while current <= high:
yield current
current += 1
for c in counter(3, 8):
print c
Wydruk będzie taki sam. Pod maską obiekt generator obsługuje protokół iteratora i robi coś mniej więcej podobnego do licznika klas.
Artykuł Davida Mertza, Iteratory i proste Generatory, jest całkiem dobrym wprowadzeniem.
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
2015-10-13 12:40:26
Istnieją cztery sposoby budowania funkcji iteracyjnej:
- Tworzenie generatora (używa słowa kluczowego yield )
- użyj wyrażenia generatora (genexp )
- tworzenie iteratora (definiuje
__iter__
oraz__next__
(lubnext
w Pythonie 2.x)) - tworzenie funkcji, którą Python może samodzielnie iterować (definiuje
__getitem__
)
Przykłady:
# generator
def uc_gen(text):
for char in text:
yield char.upper()
# generator expression
def uc_genexp(text):
return (char.upper() for char in text)
# iterator protocol
class uc_iter():
def __init__(self, text):
self.text = text
self.index = 0
def __iter__(self):
return self
def __next__(self):
try:
result = self.text[self.index].upper()
except IndexError:
raise StopIteration
self.index += 1
return result
# getitem method
class uc_getitem():
def __init__(self, text):
self.text = text
def __getitem__(self, index):
result = self.text[index].upper()
return result
Aby zobaczyć wszystkie cztery metody w działaniu:
for iterator in uc_gen, uc_genexp, uc_iter, uc_getitem:
for ch in iterator('abcde'):
print ch,
print
Które wyniki w:
A B C D E
A B C D E
A B C D E
A B C D E
Uwaga:
Dwa typy generatorów (uc_gen
i uc_genexp
) nie mogą być reversed()
; zwykły iterator (uc_iter
) potrzebuje magicznej metody __reversed__
(która musi zwrócić nowy iterator, który idzie wstecz); a iterable getitem (uc_getitem
) musi mieć magiczną metodę __len__
:
# for uc_iter
def __reversed__(self):
return reversed(self.text)
# for uc_getitem
def __len__(self)
return len(self.text)
Aby odpowiedzieć na drugorzędne pytanie pułkownika Panic o nieskończony leniwie oceniany iterator, oto te przykłady, używając każdej z czterech metod powyżej:
# generator
def even_gen():
result = 0
while True:
yield result
result += 2
# generator expression
def even_genexp():
return (num for num in even_gen()) # or even_iter or even_getitem
# not much value under these circumstances
# iterator protocol
class even_iter():
def __init__(self):
self.value = 0
def __iter__(self):
return self
def __next__(self):
next_value = self.value
self.value += 2
return next_value
# getitem method
class even_getitem():
def __getitem__(self, index):
return index * 2
import random
for iterator in even_gen, even_genexp, even_iter, even_getitem:
limit = random.randint(15, 30)
count = 0
for even in iterator():
print even,
count += 1
if count >= limit:
break
print
Co daje (przynajmniej dla mojego przykładowego biegu):
0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48 50 52 54
0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38
0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30
0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32
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-07-27 23:16:30
Po pierwsze moduł itertools jest niezwykle przydatny w różnych przypadkach, w których iterator byłby przydatny, ale oto wszystko, czego potrzebujesz, aby utworzyć iterator w Pythonie:
Czy to nie fajne? Wydajność może być użyta do zastąpienia normalnego zwrotu w funkcji. Zwraca obiekt tak samo, ale zamiast niszczyć stan i kończyć, zapisuje stan, kiedy chcesz wykonać następną iterację. Oto przykład tego w akcja pobrana bezpośrednio z listy funkcji itertools :Yield
def count(n=0):
while True:
yield n
n += 1
Jak podano w opisie funkcji (jest to funkcja count () z modułu itertools...), tworzy iterator, który zwraca kolejne liczby całkowite zaczynające się od n.
Wyrażenia generatora to zupełnie inna puszka robaków (awesome worms!). Mogą być używane zamiast rozumienia Listy do zapisywania pamięci (składanie listy tworzy listę w pamięci, która jest destroyed after use if not assigned to a variable, but generator expressions can create a Generator Object... co jest fantazyjnym sposobem na powiedzenie Iterator). Oto przykład definicji wyrażenia generatora:
gen = (n for n in xrange(0,11))
Jest to bardzo podobne do powyższej definicji iteratora, z wyjątkiem tego, że pełny zakres jest z góry określony jako od 0 do 10.
Właśnie znalazłem xrange () (zdziwiony, że wcześniej go nie widziałem...) i dodał go do powyższego przykładu. xrange () jest iterowalną Wersja range () , która ma tę zaletę, że nie tworzy wstępnie listy. Byłoby bardzo przydatne, gdybyś miał gigantyczny korpus danych do iteracji i miał tylko tyle pamięci, aby to zrobić.
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
2015-07-22 00:21:13
Widzę, że niektórzy z was robią return self
w __iter__
. Chciałem tylko zauważyć, że __iter__
sama może być generatorem (eliminując w ten sposób potrzebę __next__
i podnosząc StopIteration
wyjątki)
class range:
def __init__(self,a,b):
self.a = a
self.b = b
def __iter__(self):
i = self.a
while i < self.b:
yield i
i+=1
Oczywiście tutaj równie dobrze można zrobić generator bezpośrednio, ale dla bardziej złożonych klas może to być przydatne.
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-27 15:05:12
To pytanie dotyczy obiektów iteracyjnych, a nie iteratorów. W Pythonie sekwencje są też iterowalne, więc jednym ze sposobów na stworzenie klasy iteraowalnej jest zachowanie jej jak sekwencji, tzn. nadanie jej metod __getitem__
i __len__
. Przetestowałem to na Pythonie 2 i 3.
class CustomRange:
def __init__(self, low, high):
self.low = low
self.high = high
def __getitem__(self, item):
if item >= len(self):
raise IndexError("CustomRange index out of range")
return self.low + item
def __len__(self):
return self.high - self.low
cr = CustomRange(0, 10)
for i in cr:
print(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
2016-03-21 17:39:14
Jest to funkcja iteracyjna Bez yield
. Korzysta z funkcji iter
i zamknięcia, które utrzymuje jego stan w mutowalnym (list
) zakresie zamykającym dla Pythona 2.
def count(low, high):
counter = [0]
def tmp():
val = low + counter[0]
if val < high:
counter[0] += 1
return val
return None
return iter(tmp, None)
W Pythonie 3 Stan zamknięcia jest utrzymywany w niezmiennym zakresie, a {[6] } jest używany w zakresie lokalnym do aktualizacji zmiennej stanu.
def count(low, high):
counter = 0
def tmp():
nonlocal counter
val = low + counter
if val < high:
counter += 1
return val
return None
return iter(tmp, None)
Test;
for i in count(1,10):
print(i)
1
2
3
4
5
6
7
8
9
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-03-03 17:55:26
Jeśli szukasz czegoś krótkiego i prostego, może to ci wystarczy:
class A(object):
def __init__(self, l):
self.data = l
def __iter__(self):
return iter(self.data)
Przykład użycia:
In [3]: a = A([2,3,4])
In [4]: [i for i in a]
Out[4]: [2, 3, 4]
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-04-26 08:38:39
Wszystkie odpowiedzi na tej stronie są naprawdę świetne dla złożonego obiektu. Ale dla tych, które zawierają wbudowane typy iteracyjne jako atrybuty, jak str
, list
, set
lub dict
, lub dowolna implementacja collections.Iterable
, możesz pominąć pewne rzeczy w swojej klasie.
class Test(object):
def __init__(self, string):
self.string = string
def __iter__(self):
# since your string is already iterable
return (ch for ch in string)
Może być używany jak:
for x in Test("abcde"):
print(x)
# prints
# a
# b
# c
# d
# e
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-09-03 10:02:21
Zainspirowany odpowiedzią Matta Gregory ' ego tutaj jest nieco bardziej skomplikowany iterator, który zwróci a, b,..., z, aa, ab,..., ZZ, aaa, AAB,..., zzy, zzz
class AlphaCounter:
def __init__(self, low, high):
self.current = low
self.high = high
def __iter__(self):
return self
def __next__(self): # Python 3: def __next__(self)
alpha = ' abcdefghijklmnopqrstuvwxyz'
n_current = sum([(alpha.find(self.current[x])* 26**(len(self.current)-x-1)) for x in range(len(self.current))])
n_high = sum([(alpha.find(self.high[x])* 26**(len(self.high)-x-1)) for x in range(len(self.high))])
if n_current > n_high:
raise StopIteration
else:
increment = True
ret = ''
for x in self.current[::-1]:
if 'z' == x:
if increment:
ret += 'a'
else:
ret += 'z'
else:
if increment:
ret += alpha[alpha.find(x)+1]
increment = False
else:
ret += x
if increment:
ret += 'a'
tmp = self.current
self.current = ret[::-1]
return tmp
for c in AlphaCounter('a', 'zzz'):
print(c)
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-07-13 17:34:13