Iter возвращая себя в качестве заполнителя - PullRequest
0 голосов
/ 27 октября 2019

Я видел много примеров, когда метод __iter__ возвращает self, и я сделал свой собственный пример:

class Transactions:
    def __init__(self):
        self.t = [1,2,9,12.00]
        self.idx = 0
    def __iter__(self):
        return self
    def __next__(self):
        pos = self.idx
        self.idx += 1
        try:
            return self.t[pos]
        except IndexError:
            raise StopIteration

>>> list(iter(Transactions()))
[1, 2, 9, 12.0]

Как "возвращение self" делает объектитерация? Что именно это делает?

Ответы [ 2 ]

1 голос
/ 27 октября 2019

То, что вы делаете, это и Итератор и Итерируемый . Экземпляры класса Transactions будут иметь как __iter__, что делает его итерируемым, так и __next__, что делает его итератором.

Поэтому, когда вы возвращаете self из __iter__, выв основном это указывает на то, что возвращаемый объект является Iterable, поскольку он имеет __iter__, и, поскольку при вызове __iter__ он должен возвращать экземпляр Iterator, поэтому вы определили __next__ для того же экземпляра, чтобы он вел себя какone.

Это выделено в документации :

Итерируемый

Объект, способный возвращать свои членыодин за раз. Примеры итераций включают в себя все типы последовательностей (например, list, str и tuple) и некоторые непоследовательные типы, такие как dict, файловые объекты и объекты любых классов, которые вы определяете с помощью метода iter () или с помощью getitem () метод, который реализует семантику Sequence. Итерации можно использовать в цикле for и во многих других местах, где требуется последовательность (zip (), map (),…). Когда итеративный объект передается в качестве аргумента встроенной функции iter (), он возвращает итератор для объекта . Этот итератор хорош для одного прохода по множеству значений. При использовании итераций обычно нет необходимости вызывать iter () или иметь дело с объектами итератора самостоятельно. Оператор for делает это автоматически для вас, создавая временную безымянную переменную для хранения итератора на протяжении цикла. См. Также итератор, последовательность и генератор.

Итератор

Объект, представляющий поток данных. Повторные вызовы метода __next __ () итератора (или передача его во встроенную функцию next ()) возвращают последовательные элементы в потоке. Когда больше нет доступных данных, вместо этого вызывается исключение StopIteration . В этот момент объект итератора исчерпан, и любые дальнейшие вызовы его метода next () просто вызывают StopIteration снова. Итераторы должны иметь метод iter (), который возвращает сам объект итератора, поэтому каждый итератор также является итеративным и может использоваться в большинстве мест, где принимаются другие итерации. Одним заметным исключением является код, который пытается выполнить несколько итераций. Контейнерный объект (такой как список) создает новый новый итератор каждый раз, когда вы передаете его в функцию iter () или используете его в цикле for. Попытка сделать это с помощью итератора просто вернет тот же исчерпанный объект итератора, который использовался в предыдущем проходе итерации, и он будет выглядеть как пустой контейнер.

0 голосов
/ 27 октября 2019

Был дан хороший ответ, я хотел бы показать практический пример.

Определенный в вопросе Transaction - это итератор . Его можно повторять всего один раз.

Хотя типичная итерация имеет форму for x in ...:, давайте продолжим использовать более короткую list() для демонстрации:

>>> t=Transactions()
>>> list(t)
[1, 2, 9, 12.0]
>>> list(t)
[]
>>> list(t)
[]

Для реального классаэто не то, что люди ожидают. Чтобы каждый раз выполнять итерацию по данным, для каждой итерации должен быть создан новый итератор, что делает исходный класс итерируемым :

class TransactionsIterator:
    def __init__(self, t): 
        self.t = t 
        self.idx = 0 
    def __iter__(self):
        return self
    def __next__(self):
        pos = self.idx
        self.idx += 1
        try:
            return self.t[pos]
        except IndexError:
            raise StopIteration

class Transactions:
    def __init__(self):
        self.t = [1,2,9,12.00]
    def __iter__(self):
        return TransactionsIterator(self.t)

Это ведет себя так, как это обычно делают другие классы:

>>> t=Transactions()
>>> list(t)
[1, 2, 9, 12.0]
>>> list(t)
[1, 2, 9, 12.0]
>>> list(t)
[1, 2, 9, 12.0]
>>> list(t)

и, наконец, вам не нужно заново изобретать итератор списка, это все, что вам нужно:

class Transactions:
    def __init__(self):
        self.t = [1,2,9,12.00]
    def __iter__(self):
        return iter(self.t)

Вернуться к вопросу. Мы можем перебирать данные один раз с помощью итератора и каждый раз с помощью итерации. Весь смысл итераторов, возвращающих self в __iter__, состоит в том, что код итерации не должен различать эти два случая.

...