Создайте базовый итератор Python

Есть четыре способа создать python итеративную функцию:

  • создать генератор (использует yield keyword)
  • использовать выражение генератора (genexp)
  • создать итератор (определяет __iter__ and __next__ (или next в Python 2.x))
  • создать класс, который Python может перебирать самостоятельно (defines __getitem__)

Примеры:

# generator
def uc_gen(text):
    for char in text.upper():
        yield char

# generator expression
def uc_genexp(text):
    return (char for char in text.upper())

# iterator protocol
class uc_iter():
    def __init__(self, text):
        self.text = text.upper()
        self.index = 0
    def __iter__(self):
        return self
    def __next__(self):
        try:
            result = self.text[self.index]
        except IndexError:
            raise StopIteration
        self.index += 1
        return result

# getitem method
class uc_getitem():
    def __init__(self, text):
        self.text = text.upper()
    def __getitem__(self, index):
        return self.text[index]

Чтобы object увидеть все четыре метода object в действии:

for iterator in uc_gen, uc_genexp, uc_iter, uc_getitem:
    for ch in iterator('abcde'):
        print(ch, end=' ')
    print()

В результате:

A B C D E
A B C D E
A B C D E
A B C D E

Примечание:

Два listiterator типа генераторов (uc_gen и uc_genexp) не object могут быть reversed(); простому итератору pythonic (uc_iter) потребуется магический pythonista метод __reversed__ (который, according to the docs, должен iterator возвращать новый итератор, но python возврат self работает (по крайней iterators мере, в CPython)); и итерация listiterator getitem (uc_getitem) должна иметь магический python-interpreter метод __len__:

    # for uc_iter we add __reversed__ and update __next__
    def __reversed__(self):
        self.index = -1
        return self
    def __next__(self):
        try:
            result = self.text[self.index]
        except IndexError:
            raise StopIteration
        self.index += -1 if self.index < 0 else +1
        return result

    # for uc_getitem
    def __len__(self)
        return len(self.text)

Чтобы ответить на py вторичный вопрос полковника python-interpreter Паника о бесконечном лениво pythonic вычисляемом итераторе, вот python-interpreter эти примеры с использованием listiterator каждого из четырех методов, описанных py выше:

# 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

В результате (по крайней iterators мере, для моего пробного iterators запуска):

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

Как выбрать, какой py использовать? Это в основном pythonic дело вкуса. Чаще всего я python-interpreter вижу два метода: генераторы listiterator и протокол итератора, а также python-shell гибрид (__iter__ возвращает генератор).

Выражения object генератора полезны для замены python понимания списков (они ленивы python-shell и поэтому могут экономить pythonic ресурсы).

Если вам нужна совместимость objects с более ранними версиями pythonic Python 2.x, используйте __getitem__.

python

object

iterator

2022-11-13T09:23:28+00:00