синтаксис на основе for для пропуска вызова iter - PullRequest
0 голосов
/ 05 августа 2020

Учитывая следующий итератор:

class MyIterator:

  def __iter__(self):
    print("Calling `__iter__`")
    return self

  def __reversed__(self):
    print("Calling `__reversed__`")
    return self

  def __next__(self):
    print("Calling `__next__`")
    raise StopIteration

Существует ли на основе for эквивалентный синтаксис следующего кода:

R = reversed(MyIterator())
while True:
  try:
    next(R)
  except StopIteration:
    break

, который фактически печатает

Calling `__reversed__`
Calling `__next__`

for синтаксис, кажется, всегда добавляет вызов iter, даже если данный объект уже соблюдает протокол Iterator.

1 Ответ

3 голосов
/ 05 августа 2020

Вызов __iter__ неявно является преднамеренным; for не знает, был ли ему передан итера tor или итера ble , и необходимо безоговорочно убедиться, что у него есть итера tor , который он делает, вызывая __iter__. Если ваш объект уже является итератором, предполагается, что __iter__ идемпотентен, ничего не делает , но возвращает себя (тело должно быть только return self). В существующем коде Python есть несколько тестов, которые обнаруживают итераторы путем тестирования iter(x) is x, и если вы нарушите правила, сделав что-то иное, кроме возврата self в свой пользовательский итератор, вы несете ответственность за неправильное поведение.

Дело в том, что то, что вы хотите делать, не имеет смысла. Либо напишите итератор (без __next__ и __iter__, который возвращает новый итератор), либо напишите итератор, но если вы пишете итератор, вам нужно соблюдать правила для итераторов.

Просто для ясности, неизбежный порядок операций здесь:

  1. reversed вызывает __reversed__, который теоретически должен вернуть новый обратный итератор (очень странно, что он возвращает существующий , теоретически прямой итератор)
  2. for неявно вызывает __iter__ при любом возврате reversed, чтобы убедиться, что это итератор (если __reversed__ были реализованы правильно, он вернул бы совершенно новый итератор, но вместо этого вы вызываете __iter__ для того же объекта)
  3. for, а затем вызываете __next__ неявно для каждого элемента, пока не будет поднято StopIteration

Чтобы исправить это, попросите __reversed__ вернуть новый обратный итератор, а не self; новый итератор __iter__ по-прежнему будет вызываться, но не тот, который был на исходном итераторе. Еще лучше, разделите свои реализации; то, что обратимо, не должно быть итератором. Просто сделайте __iter__ и __reversed__ обе функции генератора, которые производят все, что они должны произвести, и избавьтесь от __next__, чтобы сделать ваш класс itera ble и обратимым, но не itera tor. .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...