Dictionary infinite loop is exiting unexpectedly
I was experimenting with various ways of creating an infinite loop in Python (other than the usual while True
), and came up with this idea:
x = {0: None}
for i in x:
del x[i]
x[i+1] = None # Value doesn't matter, so I set it to None
print(i)
On paper, I traced out the way this would infinitely loop:
- I loop through the key's value in the dictionary
- I delete that entry.
- The current counter position in the loop
+ 1
will be the new key with valueNone
which updates the dictionary. - I output the current counter.
This, in my head, should output the natural numbers in a sort of infinite loop fashion:
0
1
2
3
4
5
.
.
.
I thought this idea was clever, however when I run it on Python 3.6, it outputs:
0
1
2
3
4
Yes, it somehow stopped after 5 iterations. Clearly, there is no base condition or sentinel value in the code block of the loop, so why is Python only running this code 5 times?
python dictionary infinite-loop
add a comment |
I was experimenting with various ways of creating an infinite loop in Python (other than the usual while True
), and came up with this idea:
x = {0: None}
for i in x:
del x[i]
x[i+1] = None # Value doesn't matter, so I set it to None
print(i)
On paper, I traced out the way this would infinitely loop:
- I loop through the key's value in the dictionary
- I delete that entry.
- The current counter position in the loop
+ 1
will be the new key with valueNone
which updates the dictionary. - I output the current counter.
This, in my head, should output the natural numbers in a sort of infinite loop fashion:
0
1
2
3
4
5
.
.
.
I thought this idea was clever, however when I run it on Python 3.6, it outputs:
0
1
2
3
4
Yes, it somehow stopped after 5 iterations. Clearly, there is no base condition or sentinel value in the code block of the loop, so why is Python only running this code 5 times?
python dictionary infinite-loop
10
Mutating an object that you're iterating over is pretty much guaranteed to give you unpredictable results. In your specific case, I would guess that the4
went into a hash bucket that had already been iterated over.
– jasonharper
Jan 22 at 21:27
4
@jasonharper: CPython 3.6 is when they introduced the new compact dict implementation, so bucket locations wouldn't have this effect on 3.6. This looks like the dict needed a rebuild after the5
was inserted, which would have re-compacted the compact array of entries and shifted the entry for the5
to the left of the iterator position.
– user2357112
Jan 22 at 21:47
5
You are making unwarranted assumptions about the dictionary iterator. The only assumption that you should make is that it isn't guaranteed to work if you add/remove from the dictionary while iterating over it. What actually ends up happening will depend on various implementation details.
– juanpa.arrivillaga
Jan 22 at 21:54
add a comment |
I was experimenting with various ways of creating an infinite loop in Python (other than the usual while True
), and came up with this idea:
x = {0: None}
for i in x:
del x[i]
x[i+1] = None # Value doesn't matter, so I set it to None
print(i)
On paper, I traced out the way this would infinitely loop:
- I loop through the key's value in the dictionary
- I delete that entry.
- The current counter position in the loop
+ 1
will be the new key with valueNone
which updates the dictionary. - I output the current counter.
This, in my head, should output the natural numbers in a sort of infinite loop fashion:
0
1
2
3
4
5
.
.
.
I thought this idea was clever, however when I run it on Python 3.6, it outputs:
0
1
2
3
4
Yes, it somehow stopped after 5 iterations. Clearly, there is no base condition or sentinel value in the code block of the loop, so why is Python only running this code 5 times?
python dictionary infinite-loop
I was experimenting with various ways of creating an infinite loop in Python (other than the usual while True
), and came up with this idea:
x = {0: None}
for i in x:
del x[i]
x[i+1] = None # Value doesn't matter, so I set it to None
print(i)
On paper, I traced out the way this would infinitely loop:
- I loop through the key's value in the dictionary
- I delete that entry.
- The current counter position in the loop
+ 1
will be the new key with valueNone
which updates the dictionary. - I output the current counter.
This, in my head, should output the natural numbers in a sort of infinite loop fashion:
0
1
2
3
4
5
.
.
.
I thought this idea was clever, however when I run it on Python 3.6, it outputs:
0
1
2
3
4
Yes, it somehow stopped after 5 iterations. Clearly, there is no base condition or sentinel value in the code block of the loop, so why is Python only running this code 5 times?
python dictionary infinite-loop
python dictionary infinite-loop
edited Jan 25 at 23:55


Josh Caswell
59.2k12130177
59.2k12130177
asked Jan 22 at 21:16


Suraj KothariSuraj Kothari
972619
972619
10
Mutating an object that you're iterating over is pretty much guaranteed to give you unpredictable results. In your specific case, I would guess that the4
went into a hash bucket that had already been iterated over.
– jasonharper
Jan 22 at 21:27
4
@jasonharper: CPython 3.6 is when they introduced the new compact dict implementation, so bucket locations wouldn't have this effect on 3.6. This looks like the dict needed a rebuild after the5
was inserted, which would have re-compacted the compact array of entries and shifted the entry for the5
to the left of the iterator position.
– user2357112
Jan 22 at 21:47
5
You are making unwarranted assumptions about the dictionary iterator. The only assumption that you should make is that it isn't guaranteed to work if you add/remove from the dictionary while iterating over it. What actually ends up happening will depend on various implementation details.
– juanpa.arrivillaga
Jan 22 at 21:54
add a comment |
10
Mutating an object that you're iterating over is pretty much guaranteed to give you unpredictable results. In your specific case, I would guess that the4
went into a hash bucket that had already been iterated over.
– jasonharper
Jan 22 at 21:27
4
@jasonharper: CPython 3.6 is when they introduced the new compact dict implementation, so bucket locations wouldn't have this effect on 3.6. This looks like the dict needed a rebuild after the5
was inserted, which would have re-compacted the compact array of entries and shifted the entry for the5
to the left of the iterator position.
– user2357112
Jan 22 at 21:47
5
You are making unwarranted assumptions about the dictionary iterator. The only assumption that you should make is that it isn't guaranteed to work if you add/remove from the dictionary while iterating over it. What actually ends up happening will depend on various implementation details.
– juanpa.arrivillaga
Jan 22 at 21:54
10
10
Mutating an object that you're iterating over is pretty much guaranteed to give you unpredictable results. In your specific case, I would guess that the
4
went into a hash bucket that had already been iterated over.– jasonharper
Jan 22 at 21:27
Mutating an object that you're iterating over is pretty much guaranteed to give you unpredictable results. In your specific case, I would guess that the
4
went into a hash bucket that had already been iterated over.– jasonharper
Jan 22 at 21:27
4
4
@jasonharper: CPython 3.6 is when they introduced the new compact dict implementation, so bucket locations wouldn't have this effect on 3.6. This looks like the dict needed a rebuild after the
5
was inserted, which would have re-compacted the compact array of entries and shifted the entry for the 5
to the left of the iterator position.– user2357112
Jan 22 at 21:47
@jasonharper: CPython 3.6 is when they introduced the new compact dict implementation, so bucket locations wouldn't have this effect on 3.6. This looks like the dict needed a rebuild after the
5
was inserted, which would have re-compacted the compact array of entries and shifted the entry for the 5
to the left of the iterator position.– user2357112
Jan 22 at 21:47
5
5
You are making unwarranted assumptions about the dictionary iterator. The only assumption that you should make is that it isn't guaranteed to work if you add/remove from the dictionary while iterating over it. What actually ends up happening will depend on various implementation details.
– juanpa.arrivillaga
Jan 22 at 21:54
You are making unwarranted assumptions about the dictionary iterator. The only assumption that you should make is that it isn't guaranteed to work if you add/remove from the dictionary while iterating over it. What actually ends up happening will depend on various implementation details.
– juanpa.arrivillaga
Jan 22 at 21:54
add a comment |
4 Answers
4
active
oldest
votes
There is no guarantee that you will iterate over all your dict entries if you mutate it in your loop. From the docs:
Iterating views while adding or deleting entries in the dictionary may
raise a RuntimeError or fail to iterate over all entries.
You could create an "enumerated" infinite loop similar to your initial attempt using itertools.count()
. For example:
from itertools import count
for i in count():
print(i)
# don't run this without some mechanism to break the loop, i.e.
# if i == 10:
# break
# OUTPUT
# 0
# 1
# 2
# ...and so on
Thanks for the tip on dictionaries, but the code below was sort of unnecessary. I didn't really want an infinite loop in my code, just a fun project of coming up with odd ways, but turns out mines didn't work. Thanks anyways.
– Suraj Kothari
Jan 22 at 21:57
4
@SurajKothari - added the example in case someone else ends up looking at your question in an effort to generate an "enumerated" infinite loop (or more usefully an indefinite loop where the breakpoint is determined by some other operation at runtime) similar to your initial attempt.
– benvc
Jan 22 at 22:02
@SurajKothari you can make it work by using awhile
loop instead of afor
loop. Check out this answer for more details.
– J.Baoby
Jan 23 at 11:24
add a comment |
In this case, like @benvc wrote, this is not guaranteed to work. But in case you wonder why does it work in C-Python:
The C-Python implementation destroys the dict object after some inserts and copies it to a new space in memory. It does not care about deletions. So when this happens, the loop notices it and breaking with an exception.
Check out this link if you want to read more about this, and many other interesting python internals here.
https://github.com/satwikkansal/wtfpython#-modifying-a-dictionary-while-iterating-over-it
add a comment |
I just tested your code in python2 and python3
python3 output
0,1,2,3,4
python2
0,1,2,3,4,5,6,7
One thing comes to mind that could be going on. Either there is only a certain amount of memory being allocated in your dictionary when you create the first key value and when you delete the key value we do not allocate any memory or deallocate the memory you are just removing the value. Once all the allocated memory is used it exits. Because if you run without that del you will get this error
RuntimeError: dictionary changed size during iteration
So python creates enough memory for that key value and a few more and once it is used up theres no more memory allocated for your dictionary.
Are you sure Python 3 gives 8 iterations? Mines gave 5 (0 to 4) in Python 3.6.1
– Suraj Kothari
Jan 22 at 21:29
2
I just tested it and perhaps you have the versions the other way around. Python 2.7 gives (0 to 7) and Python 3 gives (0 to 4)
– Suraj Kothari
Jan 22 at 21:31
@SurajKothari This is implementation dependend. Python 2.7 might also be Jython instead of CPython and even CPython might exhibit different behaviour for the same version, for instance when compiled for a 32 bit system or a 64 bit system.
– BlackJack
Jan 23 at 15:29
add a comment |
As many pointed out, modifying a datastructure during iteration with a for
loop is not a good idea. The while
loop though does allow that as it re-evaluates its loop condition at each iteration (I'm impressed nobody suggested that as alternative yet). One just has to find the right loop condition. Your script would have to become:
x = {0: None}
while x:
i, _ = x.popitem()
print(i)
# to avoid infinite loop while testing
# if i == 10:
# break
x[i+1] = None
In Python, a dictionary is falsy when it is empty (see docs), so the loop will only stop if at the beginning of an iteration x
is empty.
Since the dictionary only has one key-value pair, popitem()
should be enough to get that pair and remove it from the dictionary. As the next integer is added right after the dictionary is emptied, the loop condition will never be false when evaluated thereby resulting in an infinite loop.
while len(x):
ouch! Why notwhile x:
?
– Robin
Jan 23 at 19:44
Thanks for pointing that out @Robin
– J.Baoby
Jan 23 at 21:35
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%2f54316557%2fdictionary-infinite-loop-is-exiting-unexpectedly%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
There is no guarantee that you will iterate over all your dict entries if you mutate it in your loop. From the docs:
Iterating views while adding or deleting entries in the dictionary may
raise a RuntimeError or fail to iterate over all entries.
You could create an "enumerated" infinite loop similar to your initial attempt using itertools.count()
. For example:
from itertools import count
for i in count():
print(i)
# don't run this without some mechanism to break the loop, i.e.
# if i == 10:
# break
# OUTPUT
# 0
# 1
# 2
# ...and so on
Thanks for the tip on dictionaries, but the code below was sort of unnecessary. I didn't really want an infinite loop in my code, just a fun project of coming up with odd ways, but turns out mines didn't work. Thanks anyways.
– Suraj Kothari
Jan 22 at 21:57
4
@SurajKothari - added the example in case someone else ends up looking at your question in an effort to generate an "enumerated" infinite loop (or more usefully an indefinite loop where the breakpoint is determined by some other operation at runtime) similar to your initial attempt.
– benvc
Jan 22 at 22:02
@SurajKothari you can make it work by using awhile
loop instead of afor
loop. Check out this answer for more details.
– J.Baoby
Jan 23 at 11:24
add a comment |
There is no guarantee that you will iterate over all your dict entries if you mutate it in your loop. From the docs:
Iterating views while adding or deleting entries in the dictionary may
raise a RuntimeError or fail to iterate over all entries.
You could create an "enumerated" infinite loop similar to your initial attempt using itertools.count()
. For example:
from itertools import count
for i in count():
print(i)
# don't run this without some mechanism to break the loop, i.e.
# if i == 10:
# break
# OUTPUT
# 0
# 1
# 2
# ...and so on
Thanks for the tip on dictionaries, but the code below was sort of unnecessary. I didn't really want an infinite loop in my code, just a fun project of coming up with odd ways, but turns out mines didn't work. Thanks anyways.
– Suraj Kothari
Jan 22 at 21:57
4
@SurajKothari - added the example in case someone else ends up looking at your question in an effort to generate an "enumerated" infinite loop (or more usefully an indefinite loop where the breakpoint is determined by some other operation at runtime) similar to your initial attempt.
– benvc
Jan 22 at 22:02
@SurajKothari you can make it work by using awhile
loop instead of afor
loop. Check out this answer for more details.
– J.Baoby
Jan 23 at 11:24
add a comment |
There is no guarantee that you will iterate over all your dict entries if you mutate it in your loop. From the docs:
Iterating views while adding or deleting entries in the dictionary may
raise a RuntimeError or fail to iterate over all entries.
You could create an "enumerated" infinite loop similar to your initial attempt using itertools.count()
. For example:
from itertools import count
for i in count():
print(i)
# don't run this without some mechanism to break the loop, i.e.
# if i == 10:
# break
# OUTPUT
# 0
# 1
# 2
# ...and so on
There is no guarantee that you will iterate over all your dict entries if you mutate it in your loop. From the docs:
Iterating views while adding or deleting entries in the dictionary may
raise a RuntimeError or fail to iterate over all entries.
You could create an "enumerated" infinite loop similar to your initial attempt using itertools.count()
. For example:
from itertools import count
for i in count():
print(i)
# don't run this without some mechanism to break the loop, i.e.
# if i == 10:
# break
# OUTPUT
# 0
# 1
# 2
# ...and so on
edited Jan 29 at 20:06
answered Jan 22 at 21:27
benvcbenvc
6,0291727
6,0291727
Thanks for the tip on dictionaries, but the code below was sort of unnecessary. I didn't really want an infinite loop in my code, just a fun project of coming up with odd ways, but turns out mines didn't work. Thanks anyways.
– Suraj Kothari
Jan 22 at 21:57
4
@SurajKothari - added the example in case someone else ends up looking at your question in an effort to generate an "enumerated" infinite loop (or more usefully an indefinite loop where the breakpoint is determined by some other operation at runtime) similar to your initial attempt.
– benvc
Jan 22 at 22:02
@SurajKothari you can make it work by using awhile
loop instead of afor
loop. Check out this answer for more details.
– J.Baoby
Jan 23 at 11:24
add a comment |
Thanks for the tip on dictionaries, but the code below was sort of unnecessary. I didn't really want an infinite loop in my code, just a fun project of coming up with odd ways, but turns out mines didn't work. Thanks anyways.
– Suraj Kothari
Jan 22 at 21:57
4
@SurajKothari - added the example in case someone else ends up looking at your question in an effort to generate an "enumerated" infinite loop (or more usefully an indefinite loop where the breakpoint is determined by some other operation at runtime) similar to your initial attempt.
– benvc
Jan 22 at 22:02
@SurajKothari you can make it work by using awhile
loop instead of afor
loop. Check out this answer for more details.
– J.Baoby
Jan 23 at 11:24
Thanks for the tip on dictionaries, but the code below was sort of unnecessary. I didn't really want an infinite loop in my code, just a fun project of coming up with odd ways, but turns out mines didn't work. Thanks anyways.
– Suraj Kothari
Jan 22 at 21:57
Thanks for the tip on dictionaries, but the code below was sort of unnecessary. I didn't really want an infinite loop in my code, just a fun project of coming up with odd ways, but turns out mines didn't work. Thanks anyways.
– Suraj Kothari
Jan 22 at 21:57
4
4
@SurajKothari - added the example in case someone else ends up looking at your question in an effort to generate an "enumerated" infinite loop (or more usefully an indefinite loop where the breakpoint is determined by some other operation at runtime) similar to your initial attempt.
– benvc
Jan 22 at 22:02
@SurajKothari - added the example in case someone else ends up looking at your question in an effort to generate an "enumerated" infinite loop (or more usefully an indefinite loop where the breakpoint is determined by some other operation at runtime) similar to your initial attempt.
– benvc
Jan 22 at 22:02
@SurajKothari you can make it work by using a
while
loop instead of a for
loop. Check out this answer for more details.– J.Baoby
Jan 23 at 11:24
@SurajKothari you can make it work by using a
while
loop instead of a for
loop. Check out this answer for more details.– J.Baoby
Jan 23 at 11:24
add a comment |
In this case, like @benvc wrote, this is not guaranteed to work. But in case you wonder why does it work in C-Python:
The C-Python implementation destroys the dict object after some inserts and copies it to a new space in memory. It does not care about deletions. So when this happens, the loop notices it and breaking with an exception.
Check out this link if you want to read more about this, and many other interesting python internals here.
https://github.com/satwikkansal/wtfpython#-modifying-a-dictionary-while-iterating-over-it
add a comment |
In this case, like @benvc wrote, this is not guaranteed to work. But in case you wonder why does it work in C-Python:
The C-Python implementation destroys the dict object after some inserts and copies it to a new space in memory. It does not care about deletions. So when this happens, the loop notices it and breaking with an exception.
Check out this link if you want to read more about this, and many other interesting python internals here.
https://github.com/satwikkansal/wtfpython#-modifying-a-dictionary-while-iterating-over-it
add a comment |
In this case, like @benvc wrote, this is not guaranteed to work. But in case you wonder why does it work in C-Python:
The C-Python implementation destroys the dict object after some inserts and copies it to a new space in memory. It does not care about deletions. So when this happens, the loop notices it and breaking with an exception.
Check out this link if you want to read more about this, and many other interesting python internals here.
https://github.com/satwikkansal/wtfpython#-modifying-a-dictionary-while-iterating-over-it
In this case, like @benvc wrote, this is not guaranteed to work. But in case you wonder why does it work in C-Python:
The C-Python implementation destroys the dict object after some inserts and copies it to a new space in memory. It does not care about deletions. So when this happens, the loop notices it and breaking with an exception.
Check out this link if you want to read more about this, and many other interesting python internals here.
https://github.com/satwikkansal/wtfpython#-modifying-a-dictionary-while-iterating-over-it
edited Jan 28 at 10:09
answered Jan 23 at 7:08
HWM-RockerHWM-Rocker
417817
417817
add a comment |
add a comment |
I just tested your code in python2 and python3
python3 output
0,1,2,3,4
python2
0,1,2,3,4,5,6,7
One thing comes to mind that could be going on. Either there is only a certain amount of memory being allocated in your dictionary when you create the first key value and when you delete the key value we do not allocate any memory or deallocate the memory you are just removing the value. Once all the allocated memory is used it exits. Because if you run without that del you will get this error
RuntimeError: dictionary changed size during iteration
So python creates enough memory for that key value and a few more and once it is used up theres no more memory allocated for your dictionary.
Are you sure Python 3 gives 8 iterations? Mines gave 5 (0 to 4) in Python 3.6.1
– Suraj Kothari
Jan 22 at 21:29
2
I just tested it and perhaps you have the versions the other way around. Python 2.7 gives (0 to 7) and Python 3 gives (0 to 4)
– Suraj Kothari
Jan 22 at 21:31
@SurajKothari This is implementation dependend. Python 2.7 might also be Jython instead of CPython and even CPython might exhibit different behaviour for the same version, for instance when compiled for a 32 bit system or a 64 bit system.
– BlackJack
Jan 23 at 15:29
add a comment |
I just tested your code in python2 and python3
python3 output
0,1,2,3,4
python2
0,1,2,3,4,5,6,7
One thing comes to mind that could be going on. Either there is only a certain amount of memory being allocated in your dictionary when you create the first key value and when you delete the key value we do not allocate any memory or deallocate the memory you are just removing the value. Once all the allocated memory is used it exits. Because if you run without that del you will get this error
RuntimeError: dictionary changed size during iteration
So python creates enough memory for that key value and a few more and once it is used up theres no more memory allocated for your dictionary.
Are you sure Python 3 gives 8 iterations? Mines gave 5 (0 to 4) in Python 3.6.1
– Suraj Kothari
Jan 22 at 21:29
2
I just tested it and perhaps you have the versions the other way around. Python 2.7 gives (0 to 7) and Python 3 gives (0 to 4)
– Suraj Kothari
Jan 22 at 21:31
@SurajKothari This is implementation dependend. Python 2.7 might also be Jython instead of CPython and even CPython might exhibit different behaviour for the same version, for instance when compiled for a 32 bit system or a 64 bit system.
– BlackJack
Jan 23 at 15:29
add a comment |
I just tested your code in python2 and python3
python3 output
0,1,2,3,4
python2
0,1,2,3,4,5,6,7
One thing comes to mind that could be going on. Either there is only a certain amount of memory being allocated in your dictionary when you create the first key value and when you delete the key value we do not allocate any memory or deallocate the memory you are just removing the value. Once all the allocated memory is used it exits. Because if you run without that del you will get this error
RuntimeError: dictionary changed size during iteration
So python creates enough memory for that key value and a few more and once it is used up theres no more memory allocated for your dictionary.
I just tested your code in python2 and python3
python3 output
0,1,2,3,4
python2
0,1,2,3,4,5,6,7
One thing comes to mind that could be going on. Either there is only a certain amount of memory being allocated in your dictionary when you create the first key value and when you delete the key value we do not allocate any memory or deallocate the memory you are just removing the value. Once all the allocated memory is used it exits. Because if you run without that del you will get this error
RuntimeError: dictionary changed size during iteration
So python creates enough memory for that key value and a few more and once it is used up theres no more memory allocated for your dictionary.
edited Jan 22 at 21:37
answered Jan 22 at 21:27
turtlesalldayturtlesallday
318112
318112
Are you sure Python 3 gives 8 iterations? Mines gave 5 (0 to 4) in Python 3.6.1
– Suraj Kothari
Jan 22 at 21:29
2
I just tested it and perhaps you have the versions the other way around. Python 2.7 gives (0 to 7) and Python 3 gives (0 to 4)
– Suraj Kothari
Jan 22 at 21:31
@SurajKothari This is implementation dependend. Python 2.7 might also be Jython instead of CPython and even CPython might exhibit different behaviour for the same version, for instance when compiled for a 32 bit system or a 64 bit system.
– BlackJack
Jan 23 at 15:29
add a comment |
Are you sure Python 3 gives 8 iterations? Mines gave 5 (0 to 4) in Python 3.6.1
– Suraj Kothari
Jan 22 at 21:29
2
I just tested it and perhaps you have the versions the other way around. Python 2.7 gives (0 to 7) and Python 3 gives (0 to 4)
– Suraj Kothari
Jan 22 at 21:31
@SurajKothari This is implementation dependend. Python 2.7 might also be Jython instead of CPython and even CPython might exhibit different behaviour for the same version, for instance when compiled for a 32 bit system or a 64 bit system.
– BlackJack
Jan 23 at 15:29
Are you sure Python 3 gives 8 iterations? Mines gave 5 (0 to 4) in Python 3.6.1
– Suraj Kothari
Jan 22 at 21:29
Are you sure Python 3 gives 8 iterations? Mines gave 5 (0 to 4) in Python 3.6.1
– Suraj Kothari
Jan 22 at 21:29
2
2
I just tested it and perhaps you have the versions the other way around. Python 2.7 gives (0 to 7) and Python 3 gives (0 to 4)
– Suraj Kothari
Jan 22 at 21:31
I just tested it and perhaps you have the versions the other way around. Python 2.7 gives (0 to 7) and Python 3 gives (0 to 4)
– Suraj Kothari
Jan 22 at 21:31
@SurajKothari This is implementation dependend. Python 2.7 might also be Jython instead of CPython and even CPython might exhibit different behaviour for the same version, for instance when compiled for a 32 bit system or a 64 bit system.
– BlackJack
Jan 23 at 15:29
@SurajKothari This is implementation dependend. Python 2.7 might also be Jython instead of CPython and even CPython might exhibit different behaviour for the same version, for instance when compiled for a 32 bit system or a 64 bit system.
– BlackJack
Jan 23 at 15:29
add a comment |
As many pointed out, modifying a datastructure during iteration with a for
loop is not a good idea. The while
loop though does allow that as it re-evaluates its loop condition at each iteration (I'm impressed nobody suggested that as alternative yet). One just has to find the right loop condition. Your script would have to become:
x = {0: None}
while x:
i, _ = x.popitem()
print(i)
# to avoid infinite loop while testing
# if i == 10:
# break
x[i+1] = None
In Python, a dictionary is falsy when it is empty (see docs), so the loop will only stop if at the beginning of an iteration x
is empty.
Since the dictionary only has one key-value pair, popitem()
should be enough to get that pair and remove it from the dictionary. As the next integer is added right after the dictionary is emptied, the loop condition will never be false when evaluated thereby resulting in an infinite loop.
while len(x):
ouch! Why notwhile x:
?
– Robin
Jan 23 at 19:44
Thanks for pointing that out @Robin
– J.Baoby
Jan 23 at 21:35
add a comment |
As many pointed out, modifying a datastructure during iteration with a for
loop is not a good idea. The while
loop though does allow that as it re-evaluates its loop condition at each iteration (I'm impressed nobody suggested that as alternative yet). One just has to find the right loop condition. Your script would have to become:
x = {0: None}
while x:
i, _ = x.popitem()
print(i)
# to avoid infinite loop while testing
# if i == 10:
# break
x[i+1] = None
In Python, a dictionary is falsy when it is empty (see docs), so the loop will only stop if at the beginning of an iteration x
is empty.
Since the dictionary only has one key-value pair, popitem()
should be enough to get that pair and remove it from the dictionary. As the next integer is added right after the dictionary is emptied, the loop condition will never be false when evaluated thereby resulting in an infinite loop.
while len(x):
ouch! Why notwhile x:
?
– Robin
Jan 23 at 19:44
Thanks for pointing that out @Robin
– J.Baoby
Jan 23 at 21:35
add a comment |
As many pointed out, modifying a datastructure during iteration with a for
loop is not a good idea. The while
loop though does allow that as it re-evaluates its loop condition at each iteration (I'm impressed nobody suggested that as alternative yet). One just has to find the right loop condition. Your script would have to become:
x = {0: None}
while x:
i, _ = x.popitem()
print(i)
# to avoid infinite loop while testing
# if i == 10:
# break
x[i+1] = None
In Python, a dictionary is falsy when it is empty (see docs), so the loop will only stop if at the beginning of an iteration x
is empty.
Since the dictionary only has one key-value pair, popitem()
should be enough to get that pair and remove it from the dictionary. As the next integer is added right after the dictionary is emptied, the loop condition will never be false when evaluated thereby resulting in an infinite loop.
As many pointed out, modifying a datastructure during iteration with a for
loop is not a good idea. The while
loop though does allow that as it re-evaluates its loop condition at each iteration (I'm impressed nobody suggested that as alternative yet). One just has to find the right loop condition. Your script would have to become:
x = {0: None}
while x:
i, _ = x.popitem()
print(i)
# to avoid infinite loop while testing
# if i == 10:
# break
x[i+1] = None
In Python, a dictionary is falsy when it is empty (see docs), so the loop will only stop if at the beginning of an iteration x
is empty.
Since the dictionary only has one key-value pair, popitem()
should be enough to get that pair and remove it from the dictionary. As the next integer is added right after the dictionary is emptied, the loop condition will never be false when evaluated thereby resulting in an infinite loop.
edited Jan 23 at 21:34
answered Jan 23 at 10:42
J.BaobyJ.Baoby
1,4802515
1,4802515
while len(x):
ouch! Why notwhile x:
?
– Robin
Jan 23 at 19:44
Thanks for pointing that out @Robin
– J.Baoby
Jan 23 at 21:35
add a comment |
while len(x):
ouch! Why notwhile x:
?
– Robin
Jan 23 at 19:44
Thanks for pointing that out @Robin
– J.Baoby
Jan 23 at 21:35
while len(x):
ouch! Why not while x:
?– Robin
Jan 23 at 19:44
while len(x):
ouch! Why not while x:
?– Robin
Jan 23 at 19:44
Thanks for pointing that out @Robin
– J.Baoby
Jan 23 at 21:35
Thanks for pointing that out @Robin
– J.Baoby
Jan 23 at 21:35
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.
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%2f54316557%2fdictionary-infinite-loop-is-exiting-unexpectedly%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
10
Mutating an object that you're iterating over is pretty much guaranteed to give you unpredictable results. In your specific case, I would guess that the
4
went into a hash bucket that had already been iterated over.– jasonharper
Jan 22 at 21:27
4
@jasonharper: CPython 3.6 is when they introduced the new compact dict implementation, so bucket locations wouldn't have this effect on 3.6. This looks like the dict needed a rebuild after the
5
was inserted, which would have re-compacted the compact array of entries and shifted the entry for the5
to the left of the iterator position.– user2357112
Jan 22 at 21:47
5
You are making unwarranted assumptions about the dictionary iterator. The only assumption that you should make is that it isn't guaranteed to work if you add/remove from the dictionary while iterating over it. What actually ends up happening will depend on various implementation details.
– juanpa.arrivillaga
Jan 22 at 21:54