Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does list(next(iter(())) for _ in range(1)) == []?

Why does list(next(iter(())) for _ in range(1)) return an empty list rather than raising StopIteration?

>>> next(iter(()))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> [next(iter(())) for _ in range(1)]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> list(next(iter(())) for _ in range(1))  # ?!
[]

The same thing happens with a custom function that explicitly raises StopIteration:

>>> def x():
...     raise StopIteration
... 
>>> x()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in x
StopIteration
>>> [x() for _ in range(1)]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in x
StopIteration
>>> list(x() for _ in range(1))  # ?!
[]
like image 445
1'' Avatar asked Aug 29 '16 20:08

1''


People also ask

What does next ITER ()) do in Python?

The next() function returns the next item from the iterator. If the iterator is exhausted, it returns the default value passed as an argument.

What is __ ITER __ in Python?

The __iter__() function returns an iterator for the given object (array, set, tuple, etc. or custom objects). It creates an object that can be accessed one element at a time using __next__() function, which generally comes in handy when dealing with loops. Syntax : iter(object) iter(callable, sentinel)

How do you get the next element in a for loop?

Use enumerate() function to access the next item in a list in python for a loop. The for loop allows to access the variables next to the current value of the indexing variable in the list.


2 Answers

assuming all goes well, the generator comprehension x() for _ in range(1) should raise StopIteration when it is finished iterating over range(1) to indicate that there are no more items to pack into the list.

However because x() raises StopIteration it ends up exiting early meaning this behaviour is a bug in python that is being addressed with PEP 479

In python 3.6 or using from __future__ import generator_stop in python 3.5 when a StopIteration propagates out farther it is converted into a RuntimeError so that list doesn't register it as the end of the comprehension. When this is in effect the error looks like this:

Traceback (most recent call last):
  File "/Users/Tadhg/Documents/codes/test.py", line 6, in <genexpr>
    stuff = list(x() for _ in range(1))
  File "/Users/Tadhg/Documents/codes/test.py", line 4, in x
    raise StopIteration
StopIteration

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/Users/Tadhg/Documents/codes/test.py", line 6, in <module>
    stuff = list(x() for _ in range(1))
RuntimeError: generator raised StopIteration
like image 125
Tadhg McDonald-Jensen Avatar answered Sep 19 '22 19:09

Tadhg McDonald-Jensen


The StopIteration exception is used to tell the underlying mechanism of the list function when to actually stop iterating on the iterable that has been passed to it. In your case, you're telling Python that the thing that has been passed into list() is a generator. So when the generator throws a StopIteration before generating any items, it outputs an empty list because nothing has been accumulated.

like image 43
wheaties Avatar answered Sep 21 '22 19:09

wheaties