Питон:итерация по непустому списку без предложения if оказывается пустой.Почему?
-
22-08-2019 - |
Вопрос
Как может итератор над непустой последовательностью без фильтрации и агрегации (sum()
, и т. д.), ничего не дают?
Рассмотрим простой пример:
sequence = ['a', 'b', 'c']
list((el, ord(el)) for el in sequence)
Это дает [('a', 97), ('b', 98), ('c', 99)]
как и ожидалось.
Теперь просто поменяйте местами ord(el)
out для выражения, которое извлекает первое значение из некоторого генератора, используя (...).next()
— простите за надуманный пример:
def odd_integers_up_to_length(str):
return (x for x in xrange(len(str)) if x%2==1)
list((el, odd_integers_up_to_length(el).next()) for el in sequence)
Это дает []
.Да, пустой список.Нет ('a',
вещи)
кортежи.Ничего.
Но мы не фильтруем, не агрегируем и не сокращаем.Генераторное выражение над n
объекты без фильтрации или агрегации должны давать результат n
объекты, да?Что происходит?
Решение
odd_integers_up_to_length(el).next()
вызовет StopIteration, которая не перехватывается там, но перехватывается для выражения генератора внутри него, останавливая его, не давая ничего.
посмотрите на первую итерацию, когда значение равно «a»:
>>> odd_integers_up_to_length('a').next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
Другие советы
Происходит следующее: next()
звонок вызывает StopIteration
исключение, которое поднимает стек к выражению внешнего генератора и останавливает что итерация.
А StopIteration
это обычный способ итератора сигнализировать о завершении работы.Обычно мы этого не видим, потому что обычно next()
вызов происходит внутри конструкции, которая использует итератор, например. for x in iterator
или sum(iterator)
.Но когда мы звоним next()
непосредственно, мы несем ответственность за поимку StopIteration
.Если этого не сделать, возникает утечка в абстракции, что в данном случае приводит к неожиданному поведению во внешней итерации.
Урок, я полагаю:будьте осторожны с прямыми вызовами на next()
.
str — зарезервированное ключевое слово, вам следует назвать переменную по-другому.
Я также должен был дать совет о следующем
>>> seq=['a','b','c']
>>> list((el,4) for el in seq)
[('a',4), ('b',4), ('c',4)]
Так что это не list
доставляю тебе здесь неприятности...