Is is safe to use next within a for loop in Python?
up vote
21
down vote
favorite
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
add a comment |
up vote
21
down vote
favorite
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
A question to ponder: what happens if you skip theif
condition and always callnext(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. Thepairwise
recipe in timgeb's answer is a clearer way to do this.
– Jack Aidley
Dec 14 at 9:49
add a comment |
up vote
21
down vote
favorite
up vote
21
down vote
favorite
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
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
python
asked Dec 13 at 14:37
Jack Aidley
11.3k42860
11.3k42860
A question to ponder: what happens if you skip theif
condition and always callnext(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. Thepairwise
recipe in timgeb's answer is a clearer way to do this.
– Jack Aidley
Dec 14 at 9:49
add a comment |
A question to ponder: what happens if you skip theif
condition and always callnext(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. Thepairwise
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
add a comment |
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
.
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 tozip
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 withnext
, 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 theitertools
docs handles that.
– timgeb
Dec 13 at 16:56
|
show 3 more comments
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.
Nice catch on a subtle difference between versions.
– Jack Aidley
Dec 14 at 9:56
add a comment |
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.
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
.
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 tozip
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 withnext
, 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 theitertools
docs handles that.
– timgeb
Dec 13 at 16:56
|
show 3 more comments
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
.
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 tozip
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 withnext
, 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 theitertools
docs handles that.
– timgeb
Dec 13 at 16:56
|
show 3 more comments
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
.
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
.
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 tozip
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 withnext
, 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 theitertools
docs handles that.
– timgeb
Dec 13 at 16:56
|
show 3 more comments
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 tozip
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 withnext
, 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 theitertools
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
|
show 3 more comments
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.
Nice catch on a subtle difference between versions.
– Jack Aidley
Dec 14 at 9:56
add a comment |
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.
Nice catch on a subtle difference between versions.
– Jack Aidley
Dec 14 at 9:56
add a comment |
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.
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.
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
add a comment |
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
add a comment |
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.
add a comment |
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.
add a comment |
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.
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.
edited Dec 13 at 16:50
kale
1067
1067
answered Dec 13 at 15:15
user2936599
414
414
add a comment |
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
A question to ponder: what happens if you skip the
if
condition and always callnext(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