e = Example([1, 2, 3]) # Each time through the loop, expose one of the values from e.values for value in e: print("The example object contains", value)
В более общем плане итератор должен иметь возможность контролировать, откуда берутся значения, или даже вычислять их на лету (вместо того, чтобы учитывать какой-либо конкретный атрибут экземпляра).
Переведено автоматически
Ответ 1
Объекты итератора в python соответствуют протоколу iterator, что в основном означает, что они предоставляют два метода: __iter__() и __next__().
__iter__ возвращает объект iterator и неявно вызывается в начале циклов.
__next__() Метод возвращает следующее значение и неявно вызывается при каждом увеличении цикла. Этот метод вызывает исключение StopIteration, когда больше нет возвращаемого значения, которое неявно фиксируется циклическими конструкциями для остановки итерации.
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
Примечание:
Два типа генератора (uc_gen и uc_genexp) не могут быть reversed(); простому итератору (uc_iter) потребуется __reversed__ волшебный метод (который, согласно документации, должен возвращать новый итератор, но возврат self работает (по крайней мере, в CPython)); и getitem iteratable (uc_getitem) должен иметь __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 += -1if self.index < 0else +1 return result
# for uc_getitem def__len__(self) returnlen(self.text)
Чтобы ответить на второстепенный вопрос полковника Паника о бесконечном лениво вычисляемом итераторе, вот эти примеры, использующие каждый из четырех вышеперечисленных методов:
# generator defeven_gen(): result = 0 whileTrue: yield result result += 2
# generator expression defeven_genexp(): return (num for num in even_gen()) # or even_iter or even_getitem # not much value under these circumstances
# getitem method classeven_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
How to choose which one to use? This is mostly a matter of taste. The two methods I see most often are generators and the iterator protocol, as well as a hybrid (__iter__ returning a generator).
Generator expressions are useful for replacing list comprehensions (they are lazy and so can save on resources).
If one needs compatibility with earlier Python 2.x versions use __getitem__.
Ответ 3
Я вижу, что некоторые из вас делают return self в __iter__. Я просто хотел отметить, что __iter__ сам по себе может быть генератором (таким образом устраняя необходимость в __next__ и вызывая StopIteration исключения)
classrange: 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
Конечно, здесь можно было бы создать генератор напрямую, но для более сложных классов это может быть полезно.
Ответ 4
Прежде всего, модуль itertools невероятно полезен для всевозможных случаев, в которых итератор был бы полезен, но вот все, что вам нужно для создания итератора на python:
выход
Разве это не круто? Yield может использоваться для замены обычного return в функции. Он возвращает объект точно так же, но вместо уничтожения состояния и выхода, он сохраняет состояние на тот момент, когда вы захотите выполнить следующую итерацию. Вот пример этого в действии, извлеченный непосредственно из списка функций itertools:
defcount(n=0): whileTrue: yield n n += 1
Как указано в описании функций (это функция count() из модуля itertools ...) , она создает итератор, который возвращает последовательные целые числа, начинающиеся с n.
Выражения-генераторы - это совершенно другой набор червей (потрясающие черви!). Они могут использоваться вместо понимания списка для экономии памяти (понимание списка создает список в памяти, который уничтожается после использования, если не присвоен переменной, но выражения генератора могут создавать объект генератора... это необычный способ сказать "Итератор"). Вот пример определения выражения генератора:
gen = (n for n in xrange(0,11))
Это очень похоже на наше определение итератора выше, за исключением того, что полный диапазон заранее определен как от 0 до 10.
Я только что нашел xrange() (удивлен, что не видел его раньше ...) и добавил его в приведенный выше пример. xrange() - это итерируемая версия range(), преимущество которой в том, что не требуется предварительное построение списка. Было бы очень полезно, если бы у вас был огромный массив данных для итерации и было бы не так много памяти для этого.