Несмотря на то, что я читал об этом, я до сих пор не понимаю, как работает __iter__
. Что было бы простым объяснением?
Я видел def__iter__(self): return self
. Я не вижу, как это работает, или о том, как это работает.
Несмотря на то, что я читал об этом, я до сих пор не понимаю, как работает __iter__
. Что было бы простым объяснением?
Я видел def__iter__(self): return self
. Я не вижу, как это работает, или о том, как это работает.
Итератору необходимо определить два метода: __iter__()
и __next__()
(next()
в python2). Обычно сам объект определяет метод __next__()
или next()
, поэтому он просто возвращает себя как итератор. Это создает итерабельность, которая сама по себе является итератором. Эти методы используются операторами for
и in
.
Python 3 docs: docs.python.org/3/library/stdtypes.html#iterator-types
Python 2 docs: docs.python.org/2/library/stdtypes.html#iterator-types
Как я могу сказать:
__iter__
определяет метод класса, который возвращает итератор (объект, который последовательно дает следующий элемент, содержащийся в вашем объекте).
Объект итератора, возвращаемый __iter__()
, может быть почти любым объектом, если он определяет метод next()
.
Метод next
вызывается операторами типа for ... in ...
, чтобы получить следующий элемент, а next()
должен поднимать исключение StopIteration
, когда больше нет элементов.
Что бы это ни значило, это позволяет определить, как выполняется повторение вашего объекта, а __iter__
предоставляет общий интерфейс, с которым любая другая функция python знает, как работать.
Спецификацией для def __iter__(self):
являются: он возвращает итератор. Итак, если self
- итератор, то return self
явно уместен.
"Являясь итератором" означает "имеющий метод __next__(self)
" (в Python 3, в Python 2, имя рассматриваемого метода, к сожалению, просто next
, очевидно, что сбой дизайна имени для специального метода).
В Python 2.6 и выше лучшим способом реализации итератора обычно является использование соответствующего абстрактного базового класса из стандартной библиотеки collections
module - в Python 2.6, код может быть (не забудьте вызвать метод __next__
вместо этого в Python 3):
import collections
class infinite23s(collections.Iterator):
def next(self): return 23
экземпляр этого класса будет возвращать бесконечно много копий 23
при повторении (например, itertools.repeat(23)
), поэтому цикл должен быть завершен иначе. Дело в том, что подклассификация collections.Iterator
добавляет правильный метод __iter__
от вашего имени - здесь не большая вещь, но хороший общий принцип (избегайте повторяющихся, стандартных шаблонов, таких как стандартные однострочные итераторы __iter__
- в повторение, нет добавленной стоимости и большого количества вычитаемого значения! -).
Класс, поддерживающий метод __iter__, вернет экземпляр объекта итератора: объект, поддерживающий метод next(). Этот объект будет использоваться в заявлениях "за" и "в".
В Python итератором является любой объект, поддерживающий протокол итератора. Часть этого протокола заключается в том, что объект должен иметь метод __iter__()
, который возвращает объект итератора. Я полагаю, это дает вам некоторую гибкость, так что объект может передать обязанности итератора внутреннему классу или создать какой-то специальный объект. В любом случае метод __iter__()
обычно имеет только одну строку, и эта строка часто просто return self
Другая часть протокола - это метод next()
, и именно здесь выполняется настоящая работа. Этот метод должен выяснить или создать или получить следующее, и вернуть его. Возможно, потребуется отслеживать, где он находится, чтобы в следующий раз, когда он был вызван, он действительно возвращает следующую вещь.
Когда у вас есть объект, который возвращает следующую вещь в последовательности, вы можете свернуть цикл for, который выглядит следующим образом:
myname = "Fredericus"
x = []
for i in [1,2,3,4,5,6,7,8,9,10]:
x.append(myname[i-1])
i = i + 1 # get the next i
print x
в это:
myname = "Fredericus"
x = [myname[i] for i in range(10)]
print x
Обратите внимание, что где-то там, где у нас есть код, который получает следующее значение i, потому что диапазон (10) является объектом, который ПОСЛЕДУЕТ в протокол итератора, а понимание списка - это конструкция, которая ИСПОЛЬЗУЕТ протокол итератора.
Вы также можете напрямую использовать протокол итератора. Например, при написании скриптов для обработки CSV файлов я часто пишу это:
mydata = csv.reader(open('stuff.csv')
mydata.next()
for row in mydata:
# do something with the row.
Я использую итератор напрямую, вызывая next()
, чтобы пропустить строку заголовка, а затем косвенно использовать его с помощью встроенного оператора in
в инструкции for
.