Первый цикл foreach не производит никаких изменений в массиве, как и ожидалось. Тем не менее, это заставляет $v
присваивать ссылку на каждый из элементов $a
, так что к моменту окончания первого цикла $v
является, по сути, ссылкой на $a[2]
.
Как только начинается второй цикл, $v
теперь назначается значение каждого элемента. Однако $v
уже ссылается на $a[2];
, поэтому любое присвоенное ему значение будет автоматически скопировано в последний элемент массива!
Таким образом, во время первой итерации $a[2]
будет становятся нулевыми, затем одно, а затем снова, будучи эффективно скопированы на себя. Чтобы решить эту проблему, вы всегда должны отключать переменные, которые вы используете в своих циклах foreach by-reference, или, еще лучше, избегать использования прежнего вообще.
next
в значительной степени просто вызывает __next__()
в этом случае. Вызов __next__
на вашем объекте запустит генератор и вернет его (в этот момент в магии не делается).
В этом случае вы можете быть в состоянии избегайте вообще не определять __next__
:
class MyString:
def __init__(self,s):
self.s=s
def __iter__(self):
for i in range(len(self.s)):
yield(self.s[i])
# Or...
# for item in self.s:
# yield item
Если вы хотели использовать __iter__
и __next__
(чтобы определить итератор , а не просто сделать iterable ), вы, вероятно, захотите сделать что-то вроде этого:
class MyString:
def __init__(self,s):
self.s = s
self._ix = None
def __iter__(self):
return self
def __next__(self):
if self._ix is None:
self._ix = 0
try:
item = self.s[self._ix]
except IndexError:
# Possibly reset `self._ix`?
raise StopIteration
self._ix += 1
return item
Давайте посмотрим на цель метода __next__
. Из docs :
iterator .__ next __ ()
Возвращает следующий элемент из контейнера. Если дополнительных элементов нет, вызовите исключение StopIteration.
Теперь давайте посмотрим, что делает инструкция
yield
. Другой отрывок из docs :Использование выражения yield в теле функции заставляет эту функцию быть генератором
И
Когда вызывается функция генератора, она возвращает итератор, известный как генератор.
Теперь сравните
__next__
иyield
:__next__
возвращает следующий элемент из контейнера. Но функция, содержащая ключевое словоyield
, возвращает итератор. Следовательно, использованиеyield
в методе__next__
приводит к итератору, который дает итераторы.
Если вы хотите использовать
yield
, чтобы сделать свой класс итерабельным, сделайте это в__iter__
:class MyString: def __init__(self, s): self.s = s def __iter__(self): for s in self.s: yield s
Предполагается, что метод
__iter__
возвращает итератор - и ключевое словоyield
делает это именно так.
Для полноты, вот как вы могли бы реализовать итератор с помощью метода
__next__
. Вы должны следить за состоянием итерации и возвращать соответствующее значение. Простейшим решением является, вероятно, увеличение индекса каждый раз, когда вызывается__next__
:class MyString: def __init__(self,s): self.s = s self.index = -1 def __iter__(self): return self def __next__(self): self.index += 1 if self.index >= len(self.s): raise StopIteration return self.s[self.index]
iter(mystring)
, вероятно, не должен переустанавливать итерацию ... Спасибо, что посмотрю назад. :-) – mgilson 20 June 2016 в 19:12