Как решить проблему со StopIteration через except?

Дан код, он объединяет числа из двух списков в кортеж по индексам

def generate_something_2(l1, l2):
    try:
        min_i = len(l1) if len(l1) < len(l2) else len(l2)
        for i in range(0, min_i):
            yield l1[i], l2[i]
    except StopIteration:
        pass


l3 = generate_something_2([3, 2, 1, 1], [4, 3, 2])

print(next(l3))
print(next(l3))
print(next(l3))

На выходе получается

(3, 4)
(2, 3)
(1, 2)

но если добавить ещё один print(next(l3)) то вылезает ошибка

...
   print(next(l3))
StopIteration

хотя я сделал except, но он не работает. Что бы я не писал в except StopIteration:, код всё равно продолжает выдавать ошибку. Как это исправить?


Ответы (1 шт):

Автор решения: Xander

Вы не совсем правильно понимаете логику StopIteration

Он не возникает внутри функции-генератора, поэтому внутри неё перехватить это исключение невозможно. Это исключение возникает тогда, когда когда внутри функции-генератора весь код уже отработал, а внешний код требует очередной элемент.

Если вы хотите, чтобы из функции-генератора можно было неограниченное количество раз требовать следующий элемент, то вы должны переписать её так, чтобы она всегда могла вернуть ещё один элемент, и никогда бы не дошла до конца своего кода:

def generate_something_2(l1, l2):
    min_i = len(l1) if len(l1) < len(l2) else len(l2)
    for i in range(0, min_i):
        yield l1[i], l2[i]
        
    # Вот так вот
    while True:
        yield None


l3 = generate_something_2([3, 2, 1, 1], [4, 3, 2])

print(next(l3))
print(next(l3))
print(next(l3))
print(next(l3))
print(next(l3))
print(next(l3))

# Вывод:
# (3, 4)
# (2, 3)
# (1, 2)
# None
# None
# None

Либо, если вы всё-таки хотите перехватывать StopIteration, то нужно делать это не внутри функции-генератора, а в том коде, который её использует:

def generate_something_2(l1, l2):
    min_i = len(l1) if len(l1) < len(l2) else len(l2)
    for i in range(0, min_i):
        yield l1[i], l2[i]


l3 = generate_something_2([3, 2, 1, 1], [4, 3, 2])

while True:
    try:
        print(next(l3))
    except StopIteration:
        print('Генератор исчерпан')
        break

# Вывод:
# (3, 4)
# (2, 3)
# (1, 2)
# Генератор исчерпан
→ Ссылка