Итератор как логические операторы?
я наткнулся на этот код:
def myzip(*args):
iters = map(iter, args)
while iters:
res = [next(i) for i in iters]
yield tuple(res)
Я не уверен в:
- почему понимание списка не нужно ловить StopIteration
-
как
while iters
работа, как я пробовал:x=[1,2] x=iter(x) if x: print("Still True") next(x) next(x) if x: print("Still True")
и он все еще печатает "Still True"
в обоих случаях.
автор кода также сказал, потому что map возвращает "одноразовую итерацию"в 3.X и "как только мы запустим понимание списка внутри цикла однажды, iters будет исчерпан, но все еще верно (и res будет []) навсегда". Он предложил использовать list(map(iters, args)
вместо этого, если мы используем 3.Х.
Я не уверен, как это изменение на самом деле помогает ему работать, как я думал, что даже если итераторы на StopIteration
точка все еще True
(на основе того, что я пробовал ранее).
Edit:
автор привел это в качестве примера
>>> list(myzip('abc', 'lmnop'))
[('a', 'l'), ('b', 'm'), ('c', 'n')]
2 ответов
есть несколько аспектов этого вопроса.
python-2.x
на map
возвращает list
и while iters
просто убедитесь, что код не входит в цикл, если не было *args
передается в функцию. Это потому, что пустой список является False
и не-пустой список является True
.
в случае, если нет *args
он не будет входить в цикл и неявно return
s, который затем поднимает StopIteration
.
в случае, если есть хотя бы один аргумент while iters
эквивалентно while True
и он опирается на один из итераторов, чтобы поднять StopIteration
после того, как исчерпаны. Эта StopIteration не должна быть поймана (по крайней мере, до Python 3.7), потому что вы хотите myzip
чтобы остановить, если один iterable исчерпан.
python-3.x
В Python 3 map
возвращает map
экземпляр, который всегда будет считать True
так while
цикл эквивалентен while True
.
однако в python-3 есть одна проблема.x: после итерации над map
экземпляр, как только он будет исчерпан. На первой итерации (из while
loop), который работает, как ожидалось, но на следующей итерации map
будет пустым, и он просто создаст пустой список:
>>> it = map(iter, ([1,2,3], [3,4,5]))
>>> [next(sub) for sub in it]
[1, 3]
>>> [next(sub) for sub in it]
[]
нет ничего, что могло бы поднять StopIteration
больше, поэтому он будет идти в бесконечном цикле и возвращать пустой tuple
s навсегда. Это " s также причина, по которой вы не хотите вводить while
цикл если iters
-список пуст!
это может быть исправлено (как указано) с помощью:
iters = list(map(iter, args))
общее наблюдение
просто заметка о том, как это имело бы больше смысла:
def myzip(*args):
if not args:
return
iters = [iter(arg) for arg in args] # or list(map(iter, args))
while True:
res = [next(i) for i in iters]
yield tuple(res)
если вы хотите, чтобы код был совместим с python-3.7 (спасибо @Kevin за указание на это), вам явно нужно поймать StopIteration
s. Для получения дополнительной информации см. PEP-479:
def myzip(*args):
if not args:
return
iters = [iter(arg) for arg in args] # or list(map(iter, args))
while True:
try:
res = [next(i) for i in iters]
except StopIteration:
# the StopIteration raised by next has to be catched in python-3.7+
return
yield tuple(res)
последний код также работает на Python-2.7 и Python 3.х StopIterations в python 3.7+
почему понимание списка не нужно ловить StopIteration
весь точка этого цикла для next()
поднять StopIteration
и список понимания, чтобы не поймать его. Это как этот генератор выходы; the while
цикл по существу бесконечен, условие оно испытывает стабилизировано.
таким образом генератор перестает аккуратно, когда один из входов был измученный:
>>> g = myzip([1], [2, 3])
>>> next(g)
(1, 2)
>>> next(g)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in myzip
StopIteration
на StopIteration
исключение было вызвано next()
вызов первого итератора в iters
.
в сущности, все, что while iters:
не возвращать False
если ты назвал myzip()
без входов вообще:
>>> list(myzip())
[]
в противном случае он может также использовать while True:
для всех разница.
теперь, в Python 3,map(iter, args)
возвращает итератор, а не список. Этот объект может быть повторен только один раз, но это не считается пустым и всегда будет иметь логическое значение правда. Это означает, что второй пробег через while iters:
цикл iters
- это верно, но [next(i) for i in iters]
выполняется 0 раз, next()
никогда не вызывается и не требуется StopIteration
для выхода из генератора никогда не поднимается. Вот почему автор предложил list(map(iter, args))
как обойти, чтобы захватить map
значения итератора в список.