Python `for i in iter` vs` while True; i = next (iter) `

As I understand it, both of these approaches work for working on every element in the generator:

  • let it i

    be our target operator
  • let it my_iter

    be our generator
  • let callable do_something_with

    returnNone

While Loop + StopIteratioon

try:
    while True:
        i = next(my_iter)
        do_something_with(i)
except StopIteration:
    pass

      

To understand the cycle / list

for i in my_iter:
    do_something_with(i)

      

[do_something_with(i) for i in my_iter]

      

Minor change: print(i)

replaced by do_something_with(i)

as suggested by @kojiro to eliminate the use case using interpreter mechanics.


As far as I know, these are both applicable ways to iterate over a generator, is there any reason to prefer each other?

The for loop looks better than me right now. Because of: less lines / clutter and overall readability plus plus one padding.

I can really see that the while approach is an advantage if you want to easily break the loop into specific exceptions.

+3


source to share


3 answers


the third option is definitely NOT the same as the first two. the third example creates a list, one at a time for the return value print(i)

, which turns out to be None

therefore not a very interesting list.

the first two are semantically similar. There is a slight technical difference; the while loop, as shown, does not work unless my_iter

it is, in fact, an iterator (i.e., it has a method __next__()

); for example if it is a list

. The for loop works for all iterations (has a method __iter__()

) in addition to iterators.

Correct version:

my_iter = iter(my_iterable)
try:
    while True:
        i = next(my_iter)
        print(i)
except StopIteration:
    pass

      

Now, aside from readability considerations, there is actually a technical reason why you should prefer the for loop; there is a penalty you pay (in CPython, anyway) for the number of bytecodes executed in tight inner loops. let's compare:

In [1]: def forloop(my_iter):
   ...:     for i in my_iter:
   ...:         print(i)
   ...:         

In [57]: dis.dis(forloop)
  2           0 SETUP_LOOP              24 (to 27)
              3 LOAD_FAST                0 (my_iter)
              6 GET_ITER
        >>    7 FOR_ITER                16 (to 26)
             10 STORE_FAST               1 (i)

  3          13 LOAD_GLOBAL              0 (print)
             16 LOAD_FAST                1 (i)
             19 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
             22 POP_TOP
             23 JUMP_ABSOLUTE            7
        >>   26 POP_BLOCK
        >>   27 LOAD_CONST               0 (None)
             30 RETURN_VALUE

      



7 bytecodes called in vs inner loop:

In [55]: def whileloop(my_iterable):
   ....:     my_iter = iter(my_iterable)
   ....:     try:
   ....:         while True:
   ....:             i = next(my_iter)
   ....:             print(i)
   ....:     except StopIteration:
   ....:         pass
   ....:     

In [56]: dis.dis(whileloop)
  2           0 LOAD_GLOBAL              0 (iter)
              3 LOAD_FAST                0 (my_iterable)
              6 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
              9 STORE_FAST               1 (my_iter)

  3          12 SETUP_EXCEPT            32 (to 47)

  4          15 SETUP_LOOP              25 (to 43)

  5     >>   18 LOAD_GLOBAL              1 (next)
             21 LOAD_FAST                1 (my_iter)
             24 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
             27 STORE_FAST               2 (i)

  6          30 LOAD_GLOBAL              2 (print)
             33 LOAD_FAST                2 (i)
             36 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
             39 POP_TOP
             40 JUMP_ABSOLUTE           18
        >>   43 POP_BLOCK
             44 JUMP_FORWARD            18 (to 65)

  7     >>   47 DUP_TOP
             48 LOAD_GLOBAL              3 (StopIteration)
             51 COMPARE_OP              10 (exception match)
             54 POP_JUMP_IF_FALSE       64
             57 POP_TOP
             58 POP_TOP
             59 POP_TOP

  8          60 POP_EXCEPT
             61 JUMP_FORWARD             1 (to 65)
        >>   64 END_FINALLY
        >>   65 LOAD_CONST               0 (None)
             68 RETURN_VALUE

      

9 Bytecodes in the inner loop.

However, we can do even better.

In [58]: from collections import deque

In [59]: def deqloop(my_iter):
   ....:     deque(map(print, my_iter), 0)
   ....:     

In [61]: dis.dis(deqloop)
  2           0 LOAD_GLOBAL              0 (deque)
              3 LOAD_GLOBAL              1 (map)
              6 LOAD_GLOBAL              2 (print)
              9 LOAD_FAST                0 (my_iter)
             12 CALL_FUNCTION            2 (2 positional, 0 keyword pair)
             15 LOAD_CONST               1 (0)
             18 CALL_FUNCTION            2 (2 positional, 0 keyword pair)
             21 POP_TOP
             22 LOAD_CONST               0 (None)
             25 RETURN_VALUE

      

everything happens in the C, collections.deque

, map

and print

- all built. (for cpython) so no bytecodes are executed for the loop in this case. This is only a useful optimization when the iteration step is a c-function (as is the case with print

. Otherwise, the overhead of calling a python function is greater than the overhead JUMP_ABSOLUTE

.

+5


source


The for loop is the most pythonic. Note that you can exit the for loop as well as into loops.



Don't use a list comprehension unless you need the resulting list, otherwise you are unnecessarily storing all the elements. Your comprehension of the list of examples will only work with the print function in Python 3, it will not work with the print statement in Python 2.

+3


source


I agree with you that the cycle is for

superior. As you mentioned, it's less messy and much easier to read. Programmers like to keep things as simple as possible, and the loop for

does just that. It's also best for beginner Python programmers who may not have learned try/except

. Also, as Alasdeir said, you can get out of the loop. Also, the loop while

triggers an error if you use a list if you don't use iter()

on first my_iter

.

+1


source







All Articles