Is is safe to use next within a for loop in Python?











up vote
21
down vote

favorite
4












Consider the following Python code:



b = [1,2,3,4,5,6,7]
a = iter(b)
for x in a :
if (x % 2) == 0 :
print(next(a))


Which will print 3, 5, and 7. Is the use of next on the variable being iterated on a reliable construct (you may assume that a StopIteration exception is not a concern or will be handled), or does the modification of the iterator being looped over inside the loop constitute a violation of some principle?










share|improve this question






















  • A question to ponder: what happens if you skip the if condition and always call next(a)?
    – Daniel Roseman
    Dec 13 at 14:41








  • 5




    That's fine as long as you know what you're getting into.
    – timgeb
    Dec 13 at 14:41






  • 4




    Ok, but worth commenting if others are going to be using/reading the code.
    – snakecharmerb
    Dec 13 at 14:47










  • Other than academic interest, I fail to understand why one would write code like this?
    – copper.hat
    Dec 13 at 21:15










  • @copper.hat You can see here - stackoverflow.com/questions/53762253/… - for the motivating example. The aim is to find a line in a file too large to read into memory and process the next line. The pairwise recipe in timgeb's answer is a clearer way to do this.
    – Jack Aidley
    Dec 14 at 9:49















up vote
21
down vote

favorite
4












Consider the following Python code:



b = [1,2,3,4,5,6,7]
a = iter(b)
for x in a :
if (x % 2) == 0 :
print(next(a))


Which will print 3, 5, and 7. Is the use of next on the variable being iterated on a reliable construct (you may assume that a StopIteration exception is not a concern or will be handled), or does the modification of the iterator being looped over inside the loop constitute a violation of some principle?










share|improve this question






















  • A question to ponder: what happens if you skip the if condition and always call next(a)?
    – Daniel Roseman
    Dec 13 at 14:41








  • 5




    That's fine as long as you know what you're getting into.
    – timgeb
    Dec 13 at 14:41






  • 4




    Ok, but worth commenting if others are going to be using/reading the code.
    – snakecharmerb
    Dec 13 at 14:47










  • Other than academic interest, I fail to understand why one would write code like this?
    – copper.hat
    Dec 13 at 21:15










  • @copper.hat You can see here - stackoverflow.com/questions/53762253/… - for the motivating example. The aim is to find a line in a file too large to read into memory and process the next line. The pairwise recipe in timgeb's answer is a clearer way to do this.
    – Jack Aidley
    Dec 14 at 9:49













up vote
21
down vote

favorite
4









up vote
21
down vote

favorite
4






4





Consider the following Python code:



b = [1,2,3,4,5,6,7]
a = iter(b)
for x in a :
if (x % 2) == 0 :
print(next(a))


Which will print 3, 5, and 7. Is the use of next on the variable being iterated on a reliable construct (you may assume that a StopIteration exception is not a concern or will be handled), or does the modification of the iterator being looped over inside the loop constitute a violation of some principle?










share|improve this question













Consider the following Python code:



b = [1,2,3,4,5,6,7]
a = iter(b)
for x in a :
if (x % 2) == 0 :
print(next(a))


Which will print 3, 5, and 7. Is the use of next on the variable being iterated on a reliable construct (you may assume that a StopIteration exception is not a concern or will be handled), or does the modification of the iterator being looped over inside the loop constitute a violation of some principle?







python






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Dec 13 at 14:37









Jack Aidley

11.3k42860




11.3k42860












  • A question to ponder: what happens if you skip the if condition and always call next(a)?
    – Daniel Roseman
    Dec 13 at 14:41








  • 5




    That's fine as long as you know what you're getting into.
    – timgeb
    Dec 13 at 14:41






  • 4




    Ok, but worth commenting if others are going to be using/reading the code.
    – snakecharmerb
    Dec 13 at 14:47










  • Other than academic interest, I fail to understand why one would write code like this?
    – copper.hat
    Dec 13 at 21:15










  • @copper.hat You can see here - stackoverflow.com/questions/53762253/… - for the motivating example. The aim is to find a line in a file too large to read into memory and process the next line. The pairwise recipe in timgeb's answer is a clearer way to do this.
    – Jack Aidley
    Dec 14 at 9:49


















  • A question to ponder: what happens if you skip the if condition and always call next(a)?
    – Daniel Roseman
    Dec 13 at 14:41








  • 5




    That's fine as long as you know what you're getting into.
    – timgeb
    Dec 13 at 14:41






  • 4




    Ok, but worth commenting if others are going to be using/reading the code.
    – snakecharmerb
    Dec 13 at 14:47










  • Other than academic interest, I fail to understand why one would write code like this?
    – copper.hat
    Dec 13 at 21:15










  • @copper.hat You can see here - stackoverflow.com/questions/53762253/… - for the motivating example. The aim is to find a line in a file too large to read into memory and process the next line. The pairwise recipe in timgeb's answer is a clearer way to do this.
    – Jack Aidley
    Dec 14 at 9:49
















A question to ponder: what happens if you skip the if condition and always call next(a)?
– Daniel Roseman
Dec 13 at 14:41






A question to ponder: what happens if you skip the if condition and always call next(a)?
– Daniel Roseman
Dec 13 at 14:41






5




5




That's fine as long as you know what you're getting into.
– timgeb
Dec 13 at 14:41




That's fine as long as you know what you're getting into.
– timgeb
Dec 13 at 14:41




4




4




Ok, but worth commenting if others are going to be using/reading the code.
– snakecharmerb
Dec 13 at 14:47




Ok, but worth commenting if others are going to be using/reading the code.
– snakecharmerb
Dec 13 at 14:47












Other than academic interest, I fail to understand why one would write code like this?
– copper.hat
Dec 13 at 21:15




Other than academic interest, I fail to understand why one would write code like this?
– copper.hat
Dec 13 at 21:15












@copper.hat You can see here - stackoverflow.com/questions/53762253/… - for the motivating example. The aim is to find a line in a file too large to read into memory and process the next line. The pairwise recipe in timgeb's answer is a clearer way to do this.
– Jack Aidley
Dec 14 at 9:49




@copper.hat You can see here - stackoverflow.com/questions/53762253/… - for the motivating example. The aim is to find a line in a file too large to read into memory and process the next line. The pairwise recipe in timgeb's answer is a clearer way to do this.
– Jack Aidley
Dec 14 at 9:49












3 Answers
3






active

oldest

votes

















up vote
20
down vote



accepted










There's nothing wrong here protocol-wise or in theory that would stop you from writing such code. An exhausted iterator it will throw StopIteration on every subsequent call to it.__next__, so the for loop technically won't mind if you exhaust the iterator with a next/__next__ call in the loop body.



I advise against writing such code because the program will be very hard to reason about. If the scenario gets a little more complex than what you are showing here, at least I would have to go through some inputs with pen and paper and work out what's happening.



In fact, your code snippet possibly does not even behave like you think it behaves, assuming you want to print every number that is preceded by an even number.



>>> b = [1, 2, 4, 7, 8]                                              
>>> a = iter(b)
>>> for x in a:
...: if x%2 == 0:
...: print(next(a, 'stop'))
4
stop


Why is 7 skipped although it's preceded by the even number 4?



>>>> a = iter(b)                                                      
>>>> for x in a:
...: print('for loop assigned x={}'.format(x))
...: if x%2 == 0:
...: nxt = next(a, 'stop')
...: print('if popped nxt={} from iterator'.format(nxt))
...: print(nxt)
...:
for loop assigned x=1
for loop assigned x=2
if popped nxt=4 from iterator
4
for loop assigned x=7
for loop assigned x=8
if popped nxt=stop from iterator
stop


Turns out x = 4 is never assigned by the for loop because the explicit next call popped that element from the iterator before the for loop had a chance to look at the iterator again.



That's something I'd hate to work out the details of when reading code.





If you want to iterate over an iterable (including iterators) in "(element, next_element)" pairs, use the pairwise recipe from the itertools documentation.



from itertools import tee                                         

def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = tee(iterable)
next(b, None)
return zip(a, b)


Demo:



>>> b = [1,2,3,4,5,6,7]                                               
>>> a = iter(b)
>>>
>>> for x, nxt in pairwise(a): # pairwise(b) also works
...: print(x, nxt)
1 2
2 3
3 4
4 5
5 6
6 7


In general, itertools together with its recipes provides many powerful abstractions for writing readable iteration-related code. Even more useful helpers can be found in the more_itertools module, including an implementation of pairwise.






share|improve this answer























  • The example above is, of course, a toy, you can see the motivating example here: stackoverflow.com/questions/53762253/… where the asker wants to search for a particular sequence in one line and print the next.
    – Jack Aidley
    Dec 13 at 16:30










  • @JackAidley Usually, in cases like these, I believe the pattern to zip the list with itself, offset by one is interesting. For instance, you could do : for previous, current in zip(my_list, my_list[1:]):. This prevents you from doing fancy tricks with next, and is quite readable and elegant.
    – Vincent Savard
    Dec 13 at 16:37












  • @VincentSavard This doesn't work when reading lines from a file, however.
    – Jack Aidley
    Dec 13 at 16:51










  • @JackAidley Why wouldn't it work? You can read the first two lines to create the first element, then it's just a matter of reading the file line by line.
    – Vincent Savard
    Dec 13 at 16:54






  • 5




    @VincentSavard I think Jack means that you cannot slice the iterator over lines you get from opening a file. pairwise from the itertools docs handles that.
    – timgeb
    Dec 13 at 16:56


















up vote
3
down vote













It depends what you mean by 'safe', as others have commented, it is okay, but you can imagine some contrived situations that might catch you out, for example consider this code snippet:



b = [1,2,3,4,5,6,7]
a = iter(b)
def yield_stuff():
for item in a:
print(item)
print(next(a))
yield 1

list(yield_stuff())


On Python <= 3.6 it runs and outputs:



1
2
3
4
5
6
7


But on Python 3.7 it raises RuntimeError: generator raised StopIteration. Of course this is expected if you read PEP 479 and if you're thinking about handling StopIteration anyway you might never encounter it, but I guess the use cases for calling next() inside a for loop are rare and there are normally clearer ways of re-factoring the code.






share|improve this answer





















  • Nice catch on a subtle difference between versions.
    – Jack Aidley
    Dec 14 at 9:56


















up vote
3
down vote













If you modify you code to see what happens to iterator a:



b = [1,2,3,4,5,6,7]
a = iter(b)

for x in a :
print 'x', x
if (x % 2) == 0 :
print 'a', next(a)


You will see the printout:



x 1
x 2
a 3
x 4
a 5
x 6
a 7


It means that when you are doing next(a) you are moving forward your iterator. If you need (or will need in the future) to do something else with iterator a, you will have problems. For complete safety use various recipes from itertools module. For example:



from itertools import tee, izip

def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = tee(iterable)
next(b, None)
return izip(a, b)

b = [1,2,3,4,5,6,7]
a = iter(b)
c = pairwise(a)

for x, next_x in c:
if x % 2 == 0:
print next_x


Not that here you have full control in any place of the cycle either on current iterator element, or next one.






share|improve this answer























    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53764256%2fis-is-safe-to-use-next-within-a-for-loop-in-python%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    3 Answers
    3






    active

    oldest

    votes








    3 Answers
    3






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    20
    down vote



    accepted










    There's nothing wrong here protocol-wise or in theory that would stop you from writing such code. An exhausted iterator it will throw StopIteration on every subsequent call to it.__next__, so the for loop technically won't mind if you exhaust the iterator with a next/__next__ call in the loop body.



    I advise against writing such code because the program will be very hard to reason about. If the scenario gets a little more complex than what you are showing here, at least I would have to go through some inputs with pen and paper and work out what's happening.



    In fact, your code snippet possibly does not even behave like you think it behaves, assuming you want to print every number that is preceded by an even number.



    >>> b = [1, 2, 4, 7, 8]                                              
    >>> a = iter(b)
    >>> for x in a:
    ...: if x%2 == 0:
    ...: print(next(a, 'stop'))
    4
    stop


    Why is 7 skipped although it's preceded by the even number 4?



    >>>> a = iter(b)                                                      
    >>>> for x in a:
    ...: print('for loop assigned x={}'.format(x))
    ...: if x%2 == 0:
    ...: nxt = next(a, 'stop')
    ...: print('if popped nxt={} from iterator'.format(nxt))
    ...: print(nxt)
    ...:
    for loop assigned x=1
    for loop assigned x=2
    if popped nxt=4 from iterator
    4
    for loop assigned x=7
    for loop assigned x=8
    if popped nxt=stop from iterator
    stop


    Turns out x = 4 is never assigned by the for loop because the explicit next call popped that element from the iterator before the for loop had a chance to look at the iterator again.



    That's something I'd hate to work out the details of when reading code.





    If you want to iterate over an iterable (including iterators) in "(element, next_element)" pairs, use the pairwise recipe from the itertools documentation.



    from itertools import tee                                         

    def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = tee(iterable)
    next(b, None)
    return zip(a, b)


    Demo:



    >>> b = [1,2,3,4,5,6,7]                                               
    >>> a = iter(b)
    >>>
    >>> for x, nxt in pairwise(a): # pairwise(b) also works
    ...: print(x, nxt)
    1 2
    2 3
    3 4
    4 5
    5 6
    6 7


    In general, itertools together with its recipes provides many powerful abstractions for writing readable iteration-related code. Even more useful helpers can be found in the more_itertools module, including an implementation of pairwise.






    share|improve this answer























    • The example above is, of course, a toy, you can see the motivating example here: stackoverflow.com/questions/53762253/… where the asker wants to search for a particular sequence in one line and print the next.
      – Jack Aidley
      Dec 13 at 16:30










    • @JackAidley Usually, in cases like these, I believe the pattern to zip the list with itself, offset by one is interesting. For instance, you could do : for previous, current in zip(my_list, my_list[1:]):. This prevents you from doing fancy tricks with next, and is quite readable and elegant.
      – Vincent Savard
      Dec 13 at 16:37












    • @VincentSavard This doesn't work when reading lines from a file, however.
      – Jack Aidley
      Dec 13 at 16:51










    • @JackAidley Why wouldn't it work? You can read the first two lines to create the first element, then it's just a matter of reading the file line by line.
      – Vincent Savard
      Dec 13 at 16:54






    • 5




      @VincentSavard I think Jack means that you cannot slice the iterator over lines you get from opening a file. pairwise from the itertools docs handles that.
      – timgeb
      Dec 13 at 16:56















    up vote
    20
    down vote



    accepted










    There's nothing wrong here protocol-wise or in theory that would stop you from writing such code. An exhausted iterator it will throw StopIteration on every subsequent call to it.__next__, so the for loop technically won't mind if you exhaust the iterator with a next/__next__ call in the loop body.



    I advise against writing such code because the program will be very hard to reason about. If the scenario gets a little more complex than what you are showing here, at least I would have to go through some inputs with pen and paper and work out what's happening.



    In fact, your code snippet possibly does not even behave like you think it behaves, assuming you want to print every number that is preceded by an even number.



    >>> b = [1, 2, 4, 7, 8]                                              
    >>> a = iter(b)
    >>> for x in a:
    ...: if x%2 == 0:
    ...: print(next(a, 'stop'))
    4
    stop


    Why is 7 skipped although it's preceded by the even number 4?



    >>>> a = iter(b)                                                      
    >>>> for x in a:
    ...: print('for loop assigned x={}'.format(x))
    ...: if x%2 == 0:
    ...: nxt = next(a, 'stop')
    ...: print('if popped nxt={} from iterator'.format(nxt))
    ...: print(nxt)
    ...:
    for loop assigned x=1
    for loop assigned x=2
    if popped nxt=4 from iterator
    4
    for loop assigned x=7
    for loop assigned x=8
    if popped nxt=stop from iterator
    stop


    Turns out x = 4 is never assigned by the for loop because the explicit next call popped that element from the iterator before the for loop had a chance to look at the iterator again.



    That's something I'd hate to work out the details of when reading code.





    If you want to iterate over an iterable (including iterators) in "(element, next_element)" pairs, use the pairwise recipe from the itertools documentation.



    from itertools import tee                                         

    def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = tee(iterable)
    next(b, None)
    return zip(a, b)


    Demo:



    >>> b = [1,2,3,4,5,6,7]                                               
    >>> a = iter(b)
    >>>
    >>> for x, nxt in pairwise(a): # pairwise(b) also works
    ...: print(x, nxt)
    1 2
    2 3
    3 4
    4 5
    5 6
    6 7


    In general, itertools together with its recipes provides many powerful abstractions for writing readable iteration-related code. Even more useful helpers can be found in the more_itertools module, including an implementation of pairwise.






    share|improve this answer























    • The example above is, of course, a toy, you can see the motivating example here: stackoverflow.com/questions/53762253/… where the asker wants to search for a particular sequence in one line and print the next.
      – Jack Aidley
      Dec 13 at 16:30










    • @JackAidley Usually, in cases like these, I believe the pattern to zip the list with itself, offset by one is interesting. For instance, you could do : for previous, current in zip(my_list, my_list[1:]):. This prevents you from doing fancy tricks with next, and is quite readable and elegant.
      – Vincent Savard
      Dec 13 at 16:37












    • @VincentSavard This doesn't work when reading lines from a file, however.
      – Jack Aidley
      Dec 13 at 16:51










    • @JackAidley Why wouldn't it work? You can read the first two lines to create the first element, then it's just a matter of reading the file line by line.
      – Vincent Savard
      Dec 13 at 16:54






    • 5




      @VincentSavard I think Jack means that you cannot slice the iterator over lines you get from opening a file. pairwise from the itertools docs handles that.
      – timgeb
      Dec 13 at 16:56













    up vote
    20
    down vote



    accepted







    up vote
    20
    down vote



    accepted






    There's nothing wrong here protocol-wise or in theory that would stop you from writing such code. An exhausted iterator it will throw StopIteration on every subsequent call to it.__next__, so the for loop technically won't mind if you exhaust the iterator with a next/__next__ call in the loop body.



    I advise against writing such code because the program will be very hard to reason about. If the scenario gets a little more complex than what you are showing here, at least I would have to go through some inputs with pen and paper and work out what's happening.



    In fact, your code snippet possibly does not even behave like you think it behaves, assuming you want to print every number that is preceded by an even number.



    >>> b = [1, 2, 4, 7, 8]                                              
    >>> a = iter(b)
    >>> for x in a:
    ...: if x%2 == 0:
    ...: print(next(a, 'stop'))
    4
    stop


    Why is 7 skipped although it's preceded by the even number 4?



    >>>> a = iter(b)                                                      
    >>>> for x in a:
    ...: print('for loop assigned x={}'.format(x))
    ...: if x%2 == 0:
    ...: nxt = next(a, 'stop')
    ...: print('if popped nxt={} from iterator'.format(nxt))
    ...: print(nxt)
    ...:
    for loop assigned x=1
    for loop assigned x=2
    if popped nxt=4 from iterator
    4
    for loop assigned x=7
    for loop assigned x=8
    if popped nxt=stop from iterator
    stop


    Turns out x = 4 is never assigned by the for loop because the explicit next call popped that element from the iterator before the for loop had a chance to look at the iterator again.



    That's something I'd hate to work out the details of when reading code.





    If you want to iterate over an iterable (including iterators) in "(element, next_element)" pairs, use the pairwise recipe from the itertools documentation.



    from itertools import tee                                         

    def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = tee(iterable)
    next(b, None)
    return zip(a, b)


    Demo:



    >>> b = [1,2,3,4,5,6,7]                                               
    >>> a = iter(b)
    >>>
    >>> for x, nxt in pairwise(a): # pairwise(b) also works
    ...: print(x, nxt)
    1 2
    2 3
    3 4
    4 5
    5 6
    6 7


    In general, itertools together with its recipes provides many powerful abstractions for writing readable iteration-related code. Even more useful helpers can be found in the more_itertools module, including an implementation of pairwise.






    share|improve this answer














    There's nothing wrong here protocol-wise or in theory that would stop you from writing such code. An exhausted iterator it will throw StopIteration on every subsequent call to it.__next__, so the for loop technically won't mind if you exhaust the iterator with a next/__next__ call in the loop body.



    I advise against writing such code because the program will be very hard to reason about. If the scenario gets a little more complex than what you are showing here, at least I would have to go through some inputs with pen and paper and work out what's happening.



    In fact, your code snippet possibly does not even behave like you think it behaves, assuming you want to print every number that is preceded by an even number.



    >>> b = [1, 2, 4, 7, 8]                                              
    >>> a = iter(b)
    >>> for x in a:
    ...: if x%2 == 0:
    ...: print(next(a, 'stop'))
    4
    stop


    Why is 7 skipped although it's preceded by the even number 4?



    >>>> a = iter(b)                                                      
    >>>> for x in a:
    ...: print('for loop assigned x={}'.format(x))
    ...: if x%2 == 0:
    ...: nxt = next(a, 'stop')
    ...: print('if popped nxt={} from iterator'.format(nxt))
    ...: print(nxt)
    ...:
    for loop assigned x=1
    for loop assigned x=2
    if popped nxt=4 from iterator
    4
    for loop assigned x=7
    for loop assigned x=8
    if popped nxt=stop from iterator
    stop


    Turns out x = 4 is never assigned by the for loop because the explicit next call popped that element from the iterator before the for loop had a chance to look at the iterator again.



    That's something I'd hate to work out the details of when reading code.





    If you want to iterate over an iterable (including iterators) in "(element, next_element)" pairs, use the pairwise recipe from the itertools documentation.



    from itertools import tee                                         

    def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = tee(iterable)
    next(b, None)
    return zip(a, b)


    Demo:



    >>> b = [1,2,3,4,5,6,7]                                               
    >>> a = iter(b)
    >>>
    >>> for x, nxt in pairwise(a): # pairwise(b) also works
    ...: print(x, nxt)
    1 2
    2 3
    3 4
    4 5
    5 6
    6 7


    In general, itertools together with its recipes provides many powerful abstractions for writing readable iteration-related code. Even more useful helpers can be found in the more_itertools module, including an implementation of pairwise.







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Dec 13 at 17:09

























    answered Dec 13 at 15:00









    timgeb

    48.4k116390




    48.4k116390












    • The example above is, of course, a toy, you can see the motivating example here: stackoverflow.com/questions/53762253/… where the asker wants to search for a particular sequence in one line and print the next.
      – Jack Aidley
      Dec 13 at 16:30










    • @JackAidley Usually, in cases like these, I believe the pattern to zip the list with itself, offset by one is interesting. For instance, you could do : for previous, current in zip(my_list, my_list[1:]):. This prevents you from doing fancy tricks with next, and is quite readable and elegant.
      – Vincent Savard
      Dec 13 at 16:37












    • @VincentSavard This doesn't work when reading lines from a file, however.
      – Jack Aidley
      Dec 13 at 16:51










    • @JackAidley Why wouldn't it work? You can read the first two lines to create the first element, then it's just a matter of reading the file line by line.
      – Vincent Savard
      Dec 13 at 16:54






    • 5




      @VincentSavard I think Jack means that you cannot slice the iterator over lines you get from opening a file. pairwise from the itertools docs handles that.
      – timgeb
      Dec 13 at 16:56


















    • The example above is, of course, a toy, you can see the motivating example here: stackoverflow.com/questions/53762253/… where the asker wants to search for a particular sequence in one line and print the next.
      – Jack Aidley
      Dec 13 at 16:30










    • @JackAidley Usually, in cases like these, I believe the pattern to zip the list with itself, offset by one is interesting. For instance, you could do : for previous, current in zip(my_list, my_list[1:]):. This prevents you from doing fancy tricks with next, and is quite readable and elegant.
      – Vincent Savard
      Dec 13 at 16:37












    • @VincentSavard This doesn't work when reading lines from a file, however.
      – Jack Aidley
      Dec 13 at 16:51










    • @JackAidley Why wouldn't it work? You can read the first two lines to create the first element, then it's just a matter of reading the file line by line.
      – Vincent Savard
      Dec 13 at 16:54






    • 5




      @VincentSavard I think Jack means that you cannot slice the iterator over lines you get from opening a file. pairwise from the itertools docs handles that.
      – timgeb
      Dec 13 at 16:56
















    The example above is, of course, a toy, you can see the motivating example here: stackoverflow.com/questions/53762253/… where the asker wants to search for a particular sequence in one line and print the next.
    – Jack Aidley
    Dec 13 at 16:30




    The example above is, of course, a toy, you can see the motivating example here: stackoverflow.com/questions/53762253/… where the asker wants to search for a particular sequence in one line and print the next.
    – Jack Aidley
    Dec 13 at 16:30












    @JackAidley Usually, in cases like these, I believe the pattern to zip the list with itself, offset by one is interesting. For instance, you could do : for previous, current in zip(my_list, my_list[1:]):. This prevents you from doing fancy tricks with next, and is quite readable and elegant.
    – Vincent Savard
    Dec 13 at 16:37






    @JackAidley Usually, in cases like these, I believe the pattern to zip the list with itself, offset by one is interesting. For instance, you could do : for previous, current in zip(my_list, my_list[1:]):. This prevents you from doing fancy tricks with next, and is quite readable and elegant.
    – Vincent Savard
    Dec 13 at 16:37














    @VincentSavard This doesn't work when reading lines from a file, however.
    – Jack Aidley
    Dec 13 at 16:51




    @VincentSavard This doesn't work when reading lines from a file, however.
    – Jack Aidley
    Dec 13 at 16:51












    @JackAidley Why wouldn't it work? You can read the first two lines to create the first element, then it's just a matter of reading the file line by line.
    – Vincent Savard
    Dec 13 at 16:54




    @JackAidley Why wouldn't it work? You can read the first two lines to create the first element, then it's just a matter of reading the file line by line.
    – Vincent Savard
    Dec 13 at 16:54




    5




    5




    @VincentSavard I think Jack means that you cannot slice the iterator over lines you get from opening a file. pairwise from the itertools docs handles that.
    – timgeb
    Dec 13 at 16:56




    @VincentSavard I think Jack means that you cannot slice the iterator over lines you get from opening a file. pairwise from the itertools docs handles that.
    – timgeb
    Dec 13 at 16:56












    up vote
    3
    down vote













    It depends what you mean by 'safe', as others have commented, it is okay, but you can imagine some contrived situations that might catch you out, for example consider this code snippet:



    b = [1,2,3,4,5,6,7]
    a = iter(b)
    def yield_stuff():
    for item in a:
    print(item)
    print(next(a))
    yield 1

    list(yield_stuff())


    On Python <= 3.6 it runs and outputs:



    1
    2
    3
    4
    5
    6
    7


    But on Python 3.7 it raises RuntimeError: generator raised StopIteration. Of course this is expected if you read PEP 479 and if you're thinking about handling StopIteration anyway you might never encounter it, but I guess the use cases for calling next() inside a for loop are rare and there are normally clearer ways of re-factoring the code.






    share|improve this answer





















    • Nice catch on a subtle difference between versions.
      – Jack Aidley
      Dec 14 at 9:56















    up vote
    3
    down vote













    It depends what you mean by 'safe', as others have commented, it is okay, but you can imagine some contrived situations that might catch you out, for example consider this code snippet:



    b = [1,2,3,4,5,6,7]
    a = iter(b)
    def yield_stuff():
    for item in a:
    print(item)
    print(next(a))
    yield 1

    list(yield_stuff())


    On Python <= 3.6 it runs and outputs:



    1
    2
    3
    4
    5
    6
    7


    But on Python 3.7 it raises RuntimeError: generator raised StopIteration. Of course this is expected if you read PEP 479 and if you're thinking about handling StopIteration anyway you might never encounter it, but I guess the use cases for calling next() inside a for loop are rare and there are normally clearer ways of re-factoring the code.






    share|improve this answer





















    • Nice catch on a subtle difference between versions.
      – Jack Aidley
      Dec 14 at 9:56













    up vote
    3
    down vote










    up vote
    3
    down vote









    It depends what you mean by 'safe', as others have commented, it is okay, but you can imagine some contrived situations that might catch you out, for example consider this code snippet:



    b = [1,2,3,4,5,6,7]
    a = iter(b)
    def yield_stuff():
    for item in a:
    print(item)
    print(next(a))
    yield 1

    list(yield_stuff())


    On Python <= 3.6 it runs and outputs:



    1
    2
    3
    4
    5
    6
    7


    But on Python 3.7 it raises RuntimeError: generator raised StopIteration. Of course this is expected if you read PEP 479 and if you're thinking about handling StopIteration anyway you might never encounter it, but I guess the use cases for calling next() inside a for loop are rare and there are normally clearer ways of re-factoring the code.






    share|improve this answer












    It depends what you mean by 'safe', as others have commented, it is okay, but you can imagine some contrived situations that might catch you out, for example consider this code snippet:



    b = [1,2,3,4,5,6,7]
    a = iter(b)
    def yield_stuff():
    for item in a:
    print(item)
    print(next(a))
    yield 1

    list(yield_stuff())


    On Python <= 3.6 it runs and outputs:



    1
    2
    3
    4
    5
    6
    7


    But on Python 3.7 it raises RuntimeError: generator raised StopIteration. Of course this is expected if you read PEP 479 and if you're thinking about handling StopIteration anyway you might never encounter it, but I guess the use cases for calling next() inside a for loop are rare and there are normally clearer ways of re-factoring the code.







    share|improve this answer












    share|improve this answer



    share|improve this answer










    answered Dec 13 at 15:01









    Chris_Rands

    15.2k53869




    15.2k53869












    • Nice catch on a subtle difference between versions.
      – Jack Aidley
      Dec 14 at 9:56


















    • Nice catch on a subtle difference between versions.
      – Jack Aidley
      Dec 14 at 9:56
















    Nice catch on a subtle difference between versions.
    – Jack Aidley
    Dec 14 at 9:56




    Nice catch on a subtle difference between versions.
    – Jack Aidley
    Dec 14 at 9:56










    up vote
    3
    down vote













    If you modify you code to see what happens to iterator a:



    b = [1,2,3,4,5,6,7]
    a = iter(b)

    for x in a :
    print 'x', x
    if (x % 2) == 0 :
    print 'a', next(a)


    You will see the printout:



    x 1
    x 2
    a 3
    x 4
    a 5
    x 6
    a 7


    It means that when you are doing next(a) you are moving forward your iterator. If you need (or will need in the future) to do something else with iterator a, you will have problems. For complete safety use various recipes from itertools module. For example:



    from itertools import tee, izip

    def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = tee(iterable)
    next(b, None)
    return izip(a, b)

    b = [1,2,3,4,5,6,7]
    a = iter(b)
    c = pairwise(a)

    for x, next_x in c:
    if x % 2 == 0:
    print next_x


    Not that here you have full control in any place of the cycle either on current iterator element, or next one.






    share|improve this answer



























      up vote
      3
      down vote













      If you modify you code to see what happens to iterator a:



      b = [1,2,3,4,5,6,7]
      a = iter(b)

      for x in a :
      print 'x', x
      if (x % 2) == 0 :
      print 'a', next(a)


      You will see the printout:



      x 1
      x 2
      a 3
      x 4
      a 5
      x 6
      a 7


      It means that when you are doing next(a) you are moving forward your iterator. If you need (or will need in the future) to do something else with iterator a, you will have problems. For complete safety use various recipes from itertools module. For example:



      from itertools import tee, izip

      def pairwise(iterable):
      "s -> (s0,s1), (s1,s2), (s2, s3), ..."
      a, b = tee(iterable)
      next(b, None)
      return izip(a, b)

      b = [1,2,3,4,5,6,7]
      a = iter(b)
      c = pairwise(a)

      for x, next_x in c:
      if x % 2 == 0:
      print next_x


      Not that here you have full control in any place of the cycle either on current iterator element, or next one.






      share|improve this answer

























        up vote
        3
        down vote










        up vote
        3
        down vote









        If you modify you code to see what happens to iterator a:



        b = [1,2,3,4,5,6,7]
        a = iter(b)

        for x in a :
        print 'x', x
        if (x % 2) == 0 :
        print 'a', next(a)


        You will see the printout:



        x 1
        x 2
        a 3
        x 4
        a 5
        x 6
        a 7


        It means that when you are doing next(a) you are moving forward your iterator. If you need (or will need in the future) to do something else with iterator a, you will have problems. For complete safety use various recipes from itertools module. For example:



        from itertools import tee, izip

        def pairwise(iterable):
        "s -> (s0,s1), (s1,s2), (s2, s3), ..."
        a, b = tee(iterable)
        next(b, None)
        return izip(a, b)

        b = [1,2,3,4,5,6,7]
        a = iter(b)
        c = pairwise(a)

        for x, next_x in c:
        if x % 2 == 0:
        print next_x


        Not that here you have full control in any place of the cycle either on current iterator element, or next one.






        share|improve this answer














        If you modify you code to see what happens to iterator a:



        b = [1,2,3,4,5,6,7]
        a = iter(b)

        for x in a :
        print 'x', x
        if (x % 2) == 0 :
        print 'a', next(a)


        You will see the printout:



        x 1
        x 2
        a 3
        x 4
        a 5
        x 6
        a 7


        It means that when you are doing next(a) you are moving forward your iterator. If you need (or will need in the future) to do something else with iterator a, you will have problems. For complete safety use various recipes from itertools module. For example:



        from itertools import tee, izip

        def pairwise(iterable):
        "s -> (s0,s1), (s1,s2), (s2, s3), ..."
        a, b = tee(iterable)
        next(b, None)
        return izip(a, b)

        b = [1,2,3,4,5,6,7]
        a = iter(b)
        c = pairwise(a)

        for x, next_x in c:
        if x % 2 == 0:
        print next_x


        Not that here you have full control in any place of the cycle either on current iterator element, or next one.







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Dec 13 at 16:50









        kale

        1067




        1067










        answered Dec 13 at 15:15









        user2936599

        414




        414






























            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.





            Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


            Please pay close attention to the following guidance:


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53764256%2fis-is-safe-to-use-next-within-a-for-loop-in-python%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            How did Captain America manage to do this?

            迪纳利

            南乌拉尔铁路局