Почему использование yield внутри __next __ () возвращает генераторы? [Дубликат]

Первый цикл foreach не производит никаких изменений в массиве, как и ожидалось. Тем не менее, это заставляет $v присваивать ссылку на каждый из элементов $a, так что к моменту окончания первого цикла $v является, по сути, ссылкой на $a[2].

Как только начинается второй цикл, $v теперь назначается значение каждого элемента. Однако $v уже ссылается на $a[2];, поэтому любое присвоенное ему значение будет автоматически скопировано в последний элемент массива!

Таким образом, во время первой итерации $a[2] будет становятся нулевыми, затем одно, а затем снова, будучи эффективно скопированы на себя. Чтобы решить эту проблему, вы всегда должны отключать переменные, которые вы используете в своих циклах foreach by-reference, или, еще лучше, избегать использования прежнего вообще.

9
задан Vincent Savard 20 June 2016 в 19:19
поделиться

2 ответа

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
6
ответ дан mgilson 15 August 2018 в 22:59
поделиться
  • 1
    Я не видел последнее редактирование. Спрашивал, почему он пропал без вести – Moses Koledoye 20 June 2016 в 19:11
  • 2
    @MosesKoledoye - Да, это был надзор в исходном посте, который я исправил, пока вы комментировали. Я также понял, что вызов iter(mystring), вероятно, не должен переустанавливать итерацию ... Спасибо, что посмотрю назад. :-) – mgilson 20 June 2016 в 19:12

Давайте посмотрим на цель метода __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]
2
ответ дан Aran-Fey 15 August 2018 в 22:59
поделиться
Другие вопросы по тегам:

Похожие вопросы: