Modify list and dictionary during iteration, why does it fail on dict?2019 Community Moderator ElectionWhy does Python enforce change in size during iteration for dict, but not for list?How can I iterate through a dictionary whose length changes in python?How do I sort a list of dictionaries by a value of the dictionary?What is the best way to iterate over a dictionary?Convert two lists into a dictionary in PythonPython join: why is it string.join(list) instead of list.join(string)?Why is using “for…in” with array iteration a bad idea?How to remove items from a list while iterating?Create a dictionary with list comprehension in PythonIterating over dictionaries using 'for' loopsHow to delete items from a dictionary while iterating over it?Why is [] faster than list()?
How to explain that I do not want to visit a country due to personal safety concern?
Recruiter wants very extensive technical details about all of my previous work
Can I use USB data pins as power source
How to make healing in an exploration game interesting
Is a party consisting of only a bard, a cleric, and a warlock functional long-term?
Why doesn't using two cd commands in bash script execute the second command?
If I can solve Sudoku can I solve Travelling Salesman Problem(TSP)? If yes, how?
Did Ender ever learn that he killed Stilson and/or Bonzo?
Employee lack of ownership
Dice rolling probability game
Sailing the cryptic seas
Do I need to be arrogant to get ahead?
Who is flying the vertibirds?
Are all passive ability checks floors for active ability checks?
My adviser wants to be the first author
Error in Twin Prime Conjecture
How to write cleanly even if my character uses expletive language?
Is it true that good novels will automatically sell themselves on Amazon (and so on) and there is no need for one to waste time promoting?
Define, (actually define) the "stability" and "energy" of a compound
Look at your watch and tell me what time is it. vs Look at your watch and tell me what time it is
How can I track script which gives me "command not found" right after the login?
Is it normal that my co-workers at a fitness company criticize my food choices?
Is there a higher dimension analogue of Noether's theorem?
What is a^b and (a&b)<<1?
Modify list and dictionary during iteration, why does it fail on dict?
2019 Community Moderator ElectionWhy does Python enforce change in size during iteration for dict, but not for list?How can I iterate through a dictionary whose length changes in python?How do I sort a list of dictionaries by a value of the dictionary?What is the best way to iterate over a dictionary?Convert two lists into a dictionary in PythonPython join: why is it string.join(list) instead of list.join(string)?Why is using “for…in” with array iteration a bad idea?How to remove items from a list while iterating?Create a dictionary with list comprehension in PythonIterating over dictionaries using 'for' loopsHow to delete items from a dictionary while iterating over it?Why is [] faster than list()?
Let's consider this code which iterates over a list while removing an item each iteration:
x = list(range(5))
for i in x:
print(i)
x.pop()
It will print 0, 1, 2
. Only the first three elements are printed since the last two elements in the list were removed by the first two iterations.
But if you try something similar on a dict:
y = i: i for i in range(5)
for i in y:
print(i)
y.pop(i)
It will print 0
, then raise RuntimeError: dictionary changed size during iteration
, because we are removing a key from the dictionary while iterating over it.
Of course, modifying a list during iteration is bad. But why is a RuntimeError
not raised as in the case of dictionary? Is there any good reason for this behaviour?
python list loops dictionary iteration
|
show 3 more comments
Let's consider this code which iterates over a list while removing an item each iteration:
x = list(range(5))
for i in x:
print(i)
x.pop()
It will print 0, 1, 2
. Only the first three elements are printed since the last two elements in the list were removed by the first two iterations.
But if you try something similar on a dict:
y = i: i for i in range(5)
for i in y:
print(i)
y.pop(i)
It will print 0
, then raise RuntimeError: dictionary changed size during iteration
, because we are removing a key from the dictionary while iterating over it.
Of course, modifying a list during iteration is bad. But why is a RuntimeError
not raised as in the case of dictionary? Is there any good reason for this behaviour?
python list loops dictionary iteration
2
if it could be implemented for lists, that would certainly be a life-saver.
– Jean-François Fabre
Apr 4 '18 at 8:51
1
This is something I’ve been curious about for a while too. The behaviour is documented but not justified at docs.python.org/3/reference/compound_stmts.html#for. Might just be for *handwave* performance reasons? Or a conservative choice from one point of view – allowing the operation because it can be allowed, whereas it would never make sense on dicts pre-3.6 given that they were unordered.
– Ry-♦
Apr 4 '18 at 8:52
1
I could think of cases where you would want to add to a list while iterating over it, somewhat like a queue.
– phogl
Apr 4 '18 at 8:53
2
looking at dict source code iterating on keys doesn't seem trivial, like at all.
– Jean-François Fabre
Apr 4 '18 at 8:56
it's the same behaviour forset
. I guess it has to do with consistency tables hashing vs values. lists don't have that complexity.
– Jean-François Fabre
Apr 4 '18 at 8:57
|
show 3 more comments
Let's consider this code which iterates over a list while removing an item each iteration:
x = list(range(5))
for i in x:
print(i)
x.pop()
It will print 0, 1, 2
. Only the first three elements are printed since the last two elements in the list were removed by the first two iterations.
But if you try something similar on a dict:
y = i: i for i in range(5)
for i in y:
print(i)
y.pop(i)
It will print 0
, then raise RuntimeError: dictionary changed size during iteration
, because we are removing a key from the dictionary while iterating over it.
Of course, modifying a list during iteration is bad. But why is a RuntimeError
not raised as in the case of dictionary? Is there any good reason for this behaviour?
python list loops dictionary iteration
Let's consider this code which iterates over a list while removing an item each iteration:
x = list(range(5))
for i in x:
print(i)
x.pop()
It will print 0, 1, 2
. Only the first three elements are printed since the last two elements in the list were removed by the first two iterations.
But if you try something similar on a dict:
y = i: i for i in range(5)
for i in y:
print(i)
y.pop(i)
It will print 0
, then raise RuntimeError: dictionary changed size during iteration
, because we are removing a key from the dictionary while iterating over it.
Of course, modifying a list during iteration is bad. But why is a RuntimeError
not raised as in the case of dictionary? Is there any good reason for this behaviour?
python list loops dictionary iteration
python list loops dictionary iteration
edited Dec 6 '18 at 17:46
smci
15.3k677108
15.3k677108
asked Apr 4 '18 at 8:47
ducminhducminh
8181114
8181114
2
if it could be implemented for lists, that would certainly be a life-saver.
– Jean-François Fabre
Apr 4 '18 at 8:51
1
This is something I’ve been curious about for a while too. The behaviour is documented but not justified at docs.python.org/3/reference/compound_stmts.html#for. Might just be for *handwave* performance reasons? Or a conservative choice from one point of view – allowing the operation because it can be allowed, whereas it would never make sense on dicts pre-3.6 given that they were unordered.
– Ry-♦
Apr 4 '18 at 8:52
1
I could think of cases where you would want to add to a list while iterating over it, somewhat like a queue.
– phogl
Apr 4 '18 at 8:53
2
looking at dict source code iterating on keys doesn't seem trivial, like at all.
– Jean-François Fabre
Apr 4 '18 at 8:56
it's the same behaviour forset
. I guess it has to do with consistency tables hashing vs values. lists don't have that complexity.
– Jean-François Fabre
Apr 4 '18 at 8:57
|
show 3 more comments
2
if it could be implemented for lists, that would certainly be a life-saver.
– Jean-François Fabre
Apr 4 '18 at 8:51
1
This is something I’ve been curious about for a while too. The behaviour is documented but not justified at docs.python.org/3/reference/compound_stmts.html#for. Might just be for *handwave* performance reasons? Or a conservative choice from one point of view – allowing the operation because it can be allowed, whereas it would never make sense on dicts pre-3.6 given that they were unordered.
– Ry-♦
Apr 4 '18 at 8:52
1
I could think of cases where you would want to add to a list while iterating over it, somewhat like a queue.
– phogl
Apr 4 '18 at 8:53
2
looking at dict source code iterating on keys doesn't seem trivial, like at all.
– Jean-François Fabre
Apr 4 '18 at 8:56
it's the same behaviour forset
. I guess it has to do with consistency tables hashing vs values. lists don't have that complexity.
– Jean-François Fabre
Apr 4 '18 at 8:57
2
2
if it could be implemented for lists, that would certainly be a life-saver.
– Jean-François Fabre
Apr 4 '18 at 8:51
if it could be implemented for lists, that would certainly be a life-saver.
– Jean-François Fabre
Apr 4 '18 at 8:51
1
1
This is something I’ve been curious about for a while too. The behaviour is documented but not justified at docs.python.org/3/reference/compound_stmts.html#for. Might just be for *handwave* performance reasons? Or a conservative choice from one point of view – allowing the operation because it can be allowed, whereas it would never make sense on dicts pre-3.6 given that they were unordered.
– Ry-♦
Apr 4 '18 at 8:52
This is something I’ve been curious about for a while too. The behaviour is documented but not justified at docs.python.org/3/reference/compound_stmts.html#for. Might just be for *handwave* performance reasons? Or a conservative choice from one point of view – allowing the operation because it can be allowed, whereas it would never make sense on dicts pre-3.6 given that they were unordered.
– Ry-♦
Apr 4 '18 at 8:52
1
1
I could think of cases where you would want to add to a list while iterating over it, somewhat like a queue.
– phogl
Apr 4 '18 at 8:53
I could think of cases where you would want to add to a list while iterating over it, somewhat like a queue.
– phogl
Apr 4 '18 at 8:53
2
2
looking at dict source code iterating on keys doesn't seem trivial, like at all.
– Jean-François Fabre
Apr 4 '18 at 8:56
looking at dict source code iterating on keys doesn't seem trivial, like at all.
– Jean-François Fabre
Apr 4 '18 at 8:56
it's the same behaviour for
set
. I guess it has to do with consistency tables hashing vs values. lists don't have that complexity.– Jean-François Fabre
Apr 4 '18 at 8:57
it's the same behaviour for
set
. I guess it has to do with consistency tables hashing vs values. lists don't have that complexity.– Jean-François Fabre
Apr 4 '18 at 8:57
|
show 3 more comments
3 Answers
3
active
oldest
votes
I think the reason is simple. list
s are ordered, dict
s (prior to Python 3.6/3.7) and set
s are not. So modifying a list
s as you iterate may be not advised as best practise, but it leads to consistent, reproducible, and guaranteed behaviour.
You could use this, for example let's say you wanted to split a list
with an even number of elements in half and reverse the 2nd half:
>>> lst = [0,1,2,3]
>>> lst2 = [lst.pop() for _ in lst]
>>> lst, lst2
([0, 1], [3, 2])
Of course, there are much better and more intuitive ways to perform this operation, but the point is it works.
By contrast, the behaviour for dict
s and set
s is totally implementation specific since the iteration order may change depending on the hashing.
You get a RunTimeError
with collections.OrderedDict
, presumably for consistency with the dict
behaviour. I don't think any change in the dict
behaviour is likely after Python 3.6 (where dict
s are guaranteed to maintain insertion ordered) since it would break backward compatibility for no real use cases.
Note that collections.deque
also raises a RuntimeError
in this case, despite being ordered.
note that the exception is still here with python 3.6
– Jean-François Fabre
Apr 4 '18 at 9:32
1
@Jean-FrançoisFabre yes, and I don't think that will change, added a few more thoughts into my answer
– Chris_Rands
Apr 4 '18 at 9:46
add a comment |
It wouldn't have been possible to add such a check to lists without breaking backward compatibility. For dicts, there was no such issue.
In the old, pre-iterators design, for
loops worked by calling the sequence element retrieval hook with increasing integer indices until it raised IndexError. (I would say __getitem__
, but this was back before type/class unification, so C types didn't have __getitem__
.) len
isn't even involved in this design, and there is nowhere to check for modification.
When iterators were introduced, the dict iterator had the size change check from the very first commit that introduced iterators to the language. Dicts weren't iterable at all before that, so there was no backward compatibility to break. Lists still went through the old iteration protocol, though.
When list.__iter__
was introduced, it was purely a speed optimization, not intended to be a behavioral change, and adding a modification check would have broken backward compatibility with existing code that relied on the old behavior.
Interesting, a learnt something as I often do from your answers, but would you agree with my answer also about the ordered nature of the structures being important or not?
– Chris_Rands
Mar 6 at 20:45
2
@Chris_Rands: Well, it definitely makes list modification during iteration a lot more predictable than what can happen with dicts, but the behavior is still weird and not very useful. It's also inefficient or impossible to replicate the same behavior with other ordered data structures that don't support random access by index. I wouldn't consider the ordering to be a convincing reason for lists to support modification during iteration.
– user2357112
Mar 6 at 20:51
Thanks, I guess the followup question is, why did't they take the opportunity to change this when introducing Python 3 (where breaking backward-compatibility would have been okay)?
– Chris_Rands
Mar 7 at 9:25
@Chris_Rands: I don't have mailing list discussions to cite or anything, so I can only offer generic speculation: probably there was no one to push for it and do the work, or they tried it and decided the performance hit wasn't worth it.
– user2357112
Mar 8 at 8:01
add a comment |
Dictionary uses insertion order with an additional level of indirection, which causes hiccups when iterating while keys are removed and re-inserted, thereby changing the order and internal pointers of the dictionary.
And this problem is not fixed by iterating d.keys()
instead of d
, since in Python 3, d.keys()
returns a dynamic view of the keys in the dict
which results in the same problem. Instead, iterate over list(d)
as this will produce a list from the keys of the dictionary that will not change during iteration
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%2f49646583%2fmodify-list-and-dictionary-during-iteration-why-does-it-fail-on-dict%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
I think the reason is simple. list
s are ordered, dict
s (prior to Python 3.6/3.7) and set
s are not. So modifying a list
s as you iterate may be not advised as best practise, but it leads to consistent, reproducible, and guaranteed behaviour.
You could use this, for example let's say you wanted to split a list
with an even number of elements in half and reverse the 2nd half:
>>> lst = [0,1,2,3]
>>> lst2 = [lst.pop() for _ in lst]
>>> lst, lst2
([0, 1], [3, 2])
Of course, there are much better and more intuitive ways to perform this operation, but the point is it works.
By contrast, the behaviour for dict
s and set
s is totally implementation specific since the iteration order may change depending on the hashing.
You get a RunTimeError
with collections.OrderedDict
, presumably for consistency with the dict
behaviour. I don't think any change in the dict
behaviour is likely after Python 3.6 (where dict
s are guaranteed to maintain insertion ordered) since it would break backward compatibility for no real use cases.
Note that collections.deque
also raises a RuntimeError
in this case, despite being ordered.
note that the exception is still here with python 3.6
– Jean-François Fabre
Apr 4 '18 at 9:32
1
@Jean-FrançoisFabre yes, and I don't think that will change, added a few more thoughts into my answer
– Chris_Rands
Apr 4 '18 at 9:46
add a comment |
I think the reason is simple. list
s are ordered, dict
s (prior to Python 3.6/3.7) and set
s are not. So modifying a list
s as you iterate may be not advised as best practise, but it leads to consistent, reproducible, and guaranteed behaviour.
You could use this, for example let's say you wanted to split a list
with an even number of elements in half and reverse the 2nd half:
>>> lst = [0,1,2,3]
>>> lst2 = [lst.pop() for _ in lst]
>>> lst, lst2
([0, 1], [3, 2])
Of course, there are much better and more intuitive ways to perform this operation, but the point is it works.
By contrast, the behaviour for dict
s and set
s is totally implementation specific since the iteration order may change depending on the hashing.
You get a RunTimeError
with collections.OrderedDict
, presumably for consistency with the dict
behaviour. I don't think any change in the dict
behaviour is likely after Python 3.6 (where dict
s are guaranteed to maintain insertion ordered) since it would break backward compatibility for no real use cases.
Note that collections.deque
also raises a RuntimeError
in this case, despite being ordered.
note that the exception is still here with python 3.6
– Jean-François Fabre
Apr 4 '18 at 9:32
1
@Jean-FrançoisFabre yes, and I don't think that will change, added a few more thoughts into my answer
– Chris_Rands
Apr 4 '18 at 9:46
add a comment |
I think the reason is simple. list
s are ordered, dict
s (prior to Python 3.6/3.7) and set
s are not. So modifying a list
s as you iterate may be not advised as best practise, but it leads to consistent, reproducible, and guaranteed behaviour.
You could use this, for example let's say you wanted to split a list
with an even number of elements in half and reverse the 2nd half:
>>> lst = [0,1,2,3]
>>> lst2 = [lst.pop() for _ in lst]
>>> lst, lst2
([0, 1], [3, 2])
Of course, there are much better and more intuitive ways to perform this operation, but the point is it works.
By contrast, the behaviour for dict
s and set
s is totally implementation specific since the iteration order may change depending on the hashing.
You get a RunTimeError
with collections.OrderedDict
, presumably for consistency with the dict
behaviour. I don't think any change in the dict
behaviour is likely after Python 3.6 (where dict
s are guaranteed to maintain insertion ordered) since it would break backward compatibility for no real use cases.
Note that collections.deque
also raises a RuntimeError
in this case, despite being ordered.
I think the reason is simple. list
s are ordered, dict
s (prior to Python 3.6/3.7) and set
s are not. So modifying a list
s as you iterate may be not advised as best practise, but it leads to consistent, reproducible, and guaranteed behaviour.
You could use this, for example let's say you wanted to split a list
with an even number of elements in half and reverse the 2nd half:
>>> lst = [0,1,2,3]
>>> lst2 = [lst.pop() for _ in lst]
>>> lst, lst2
([0, 1], [3, 2])
Of course, there are much better and more intuitive ways to perform this operation, but the point is it works.
By contrast, the behaviour for dict
s and set
s is totally implementation specific since the iteration order may change depending on the hashing.
You get a RunTimeError
with collections.OrderedDict
, presumably for consistency with the dict
behaviour. I don't think any change in the dict
behaviour is likely after Python 3.6 (where dict
s are guaranteed to maintain insertion ordered) since it would break backward compatibility for no real use cases.
Note that collections.deque
also raises a RuntimeError
in this case, despite being ordered.
edited Apr 4 '18 at 12:57
answered Apr 4 '18 at 9:22
Chris_RandsChris_Rands
17k54176
17k54176
note that the exception is still here with python 3.6
– Jean-François Fabre
Apr 4 '18 at 9:32
1
@Jean-FrançoisFabre yes, and I don't think that will change, added a few more thoughts into my answer
– Chris_Rands
Apr 4 '18 at 9:46
add a comment |
note that the exception is still here with python 3.6
– Jean-François Fabre
Apr 4 '18 at 9:32
1
@Jean-FrançoisFabre yes, and I don't think that will change, added a few more thoughts into my answer
– Chris_Rands
Apr 4 '18 at 9:46
note that the exception is still here with python 3.6
– Jean-François Fabre
Apr 4 '18 at 9:32
note that the exception is still here with python 3.6
– Jean-François Fabre
Apr 4 '18 at 9:32
1
1
@Jean-FrançoisFabre yes, and I don't think that will change, added a few more thoughts into my answer
– Chris_Rands
Apr 4 '18 at 9:46
@Jean-FrançoisFabre yes, and I don't think that will change, added a few more thoughts into my answer
– Chris_Rands
Apr 4 '18 at 9:46
add a comment |
It wouldn't have been possible to add such a check to lists without breaking backward compatibility. For dicts, there was no such issue.
In the old, pre-iterators design, for
loops worked by calling the sequence element retrieval hook with increasing integer indices until it raised IndexError. (I would say __getitem__
, but this was back before type/class unification, so C types didn't have __getitem__
.) len
isn't even involved in this design, and there is nowhere to check for modification.
When iterators were introduced, the dict iterator had the size change check from the very first commit that introduced iterators to the language. Dicts weren't iterable at all before that, so there was no backward compatibility to break. Lists still went through the old iteration protocol, though.
When list.__iter__
was introduced, it was purely a speed optimization, not intended to be a behavioral change, and adding a modification check would have broken backward compatibility with existing code that relied on the old behavior.
Interesting, a learnt something as I often do from your answers, but would you agree with my answer also about the ordered nature of the structures being important or not?
– Chris_Rands
Mar 6 at 20:45
2
@Chris_Rands: Well, it definitely makes list modification during iteration a lot more predictable than what can happen with dicts, but the behavior is still weird and not very useful. It's also inefficient or impossible to replicate the same behavior with other ordered data structures that don't support random access by index. I wouldn't consider the ordering to be a convincing reason for lists to support modification during iteration.
– user2357112
Mar 6 at 20:51
Thanks, I guess the followup question is, why did't they take the opportunity to change this when introducing Python 3 (where breaking backward-compatibility would have been okay)?
– Chris_Rands
Mar 7 at 9:25
@Chris_Rands: I don't have mailing list discussions to cite or anything, so I can only offer generic speculation: probably there was no one to push for it and do the work, or they tried it and decided the performance hit wasn't worth it.
– user2357112
Mar 8 at 8:01
add a comment |
It wouldn't have been possible to add such a check to lists without breaking backward compatibility. For dicts, there was no such issue.
In the old, pre-iterators design, for
loops worked by calling the sequence element retrieval hook with increasing integer indices until it raised IndexError. (I would say __getitem__
, but this was back before type/class unification, so C types didn't have __getitem__
.) len
isn't even involved in this design, and there is nowhere to check for modification.
When iterators were introduced, the dict iterator had the size change check from the very first commit that introduced iterators to the language. Dicts weren't iterable at all before that, so there was no backward compatibility to break. Lists still went through the old iteration protocol, though.
When list.__iter__
was introduced, it was purely a speed optimization, not intended to be a behavioral change, and adding a modification check would have broken backward compatibility with existing code that relied on the old behavior.
Interesting, a learnt something as I often do from your answers, but would you agree with my answer also about the ordered nature of the structures being important or not?
– Chris_Rands
Mar 6 at 20:45
2
@Chris_Rands: Well, it definitely makes list modification during iteration a lot more predictable than what can happen with dicts, but the behavior is still weird and not very useful. It's also inefficient or impossible to replicate the same behavior with other ordered data structures that don't support random access by index. I wouldn't consider the ordering to be a convincing reason for lists to support modification during iteration.
– user2357112
Mar 6 at 20:51
Thanks, I guess the followup question is, why did't they take the opportunity to change this when introducing Python 3 (where breaking backward-compatibility would have been okay)?
– Chris_Rands
Mar 7 at 9:25
@Chris_Rands: I don't have mailing list discussions to cite or anything, so I can only offer generic speculation: probably there was no one to push for it and do the work, or they tried it and decided the performance hit wasn't worth it.
– user2357112
Mar 8 at 8:01
add a comment |
It wouldn't have been possible to add such a check to lists without breaking backward compatibility. For dicts, there was no such issue.
In the old, pre-iterators design, for
loops worked by calling the sequence element retrieval hook with increasing integer indices until it raised IndexError. (I would say __getitem__
, but this was back before type/class unification, so C types didn't have __getitem__
.) len
isn't even involved in this design, and there is nowhere to check for modification.
When iterators were introduced, the dict iterator had the size change check from the very first commit that introduced iterators to the language. Dicts weren't iterable at all before that, so there was no backward compatibility to break. Lists still went through the old iteration protocol, though.
When list.__iter__
was introduced, it was purely a speed optimization, not intended to be a behavioral change, and adding a modification check would have broken backward compatibility with existing code that relied on the old behavior.
It wouldn't have been possible to add such a check to lists without breaking backward compatibility. For dicts, there was no such issue.
In the old, pre-iterators design, for
loops worked by calling the sequence element retrieval hook with increasing integer indices until it raised IndexError. (I would say __getitem__
, but this was back before type/class unification, so C types didn't have __getitem__
.) len
isn't even involved in this design, and there is nowhere to check for modification.
When iterators were introduced, the dict iterator had the size change check from the very first commit that introduced iterators to the language. Dicts weren't iterable at all before that, so there was no backward compatibility to break. Lists still went through the old iteration protocol, though.
When list.__iter__
was introduced, it was purely a speed optimization, not intended to be a behavioral change, and adding a modification check would have broken backward compatibility with existing code that relied on the old behavior.
answered Mar 6 at 20:38
user2357112user2357112
156k12170264
156k12170264
Interesting, a learnt something as I often do from your answers, but would you agree with my answer also about the ordered nature of the structures being important or not?
– Chris_Rands
Mar 6 at 20:45
2
@Chris_Rands: Well, it definitely makes list modification during iteration a lot more predictable than what can happen with dicts, but the behavior is still weird and not very useful. It's also inefficient or impossible to replicate the same behavior with other ordered data structures that don't support random access by index. I wouldn't consider the ordering to be a convincing reason for lists to support modification during iteration.
– user2357112
Mar 6 at 20:51
Thanks, I guess the followup question is, why did't they take the opportunity to change this when introducing Python 3 (where breaking backward-compatibility would have been okay)?
– Chris_Rands
Mar 7 at 9:25
@Chris_Rands: I don't have mailing list discussions to cite or anything, so I can only offer generic speculation: probably there was no one to push for it and do the work, or they tried it and decided the performance hit wasn't worth it.
– user2357112
Mar 8 at 8:01
add a comment |
Interesting, a learnt something as I often do from your answers, but would you agree with my answer also about the ordered nature of the structures being important or not?
– Chris_Rands
Mar 6 at 20:45
2
@Chris_Rands: Well, it definitely makes list modification during iteration a lot more predictable than what can happen with dicts, but the behavior is still weird and not very useful. It's also inefficient or impossible to replicate the same behavior with other ordered data structures that don't support random access by index. I wouldn't consider the ordering to be a convincing reason for lists to support modification during iteration.
– user2357112
Mar 6 at 20:51
Thanks, I guess the followup question is, why did't they take the opportunity to change this when introducing Python 3 (where breaking backward-compatibility would have been okay)?
– Chris_Rands
Mar 7 at 9:25
@Chris_Rands: I don't have mailing list discussions to cite or anything, so I can only offer generic speculation: probably there was no one to push for it and do the work, or they tried it and decided the performance hit wasn't worth it.
– user2357112
Mar 8 at 8:01
Interesting, a learnt something as I often do from your answers, but would you agree with my answer also about the ordered nature of the structures being important or not?
– Chris_Rands
Mar 6 at 20:45
Interesting, a learnt something as I often do from your answers, but would you agree with my answer also about the ordered nature of the structures being important or not?
– Chris_Rands
Mar 6 at 20:45
2
2
@Chris_Rands: Well, it definitely makes list modification during iteration a lot more predictable than what can happen with dicts, but the behavior is still weird and not very useful. It's also inefficient or impossible to replicate the same behavior with other ordered data structures that don't support random access by index. I wouldn't consider the ordering to be a convincing reason for lists to support modification during iteration.
– user2357112
Mar 6 at 20:51
@Chris_Rands: Well, it definitely makes list modification during iteration a lot more predictable than what can happen with dicts, but the behavior is still weird and not very useful. It's also inefficient or impossible to replicate the same behavior with other ordered data structures that don't support random access by index. I wouldn't consider the ordering to be a convincing reason for lists to support modification during iteration.
– user2357112
Mar 6 at 20:51
Thanks, I guess the followup question is, why did't they take the opportunity to change this when introducing Python 3 (where breaking backward-compatibility would have been okay)?
– Chris_Rands
Mar 7 at 9:25
Thanks, I guess the followup question is, why did't they take the opportunity to change this when introducing Python 3 (where breaking backward-compatibility would have been okay)?
– Chris_Rands
Mar 7 at 9:25
@Chris_Rands: I don't have mailing list discussions to cite or anything, so I can only offer generic speculation: probably there was no one to push for it and do the work, or they tried it and decided the performance hit wasn't worth it.
– user2357112
Mar 8 at 8:01
@Chris_Rands: I don't have mailing list discussions to cite or anything, so I can only offer generic speculation: probably there was no one to push for it and do the work, or they tried it and decided the performance hit wasn't worth it.
– user2357112
Mar 8 at 8:01
add a comment |
Dictionary uses insertion order with an additional level of indirection, which causes hiccups when iterating while keys are removed and re-inserted, thereby changing the order and internal pointers of the dictionary.
And this problem is not fixed by iterating d.keys()
instead of d
, since in Python 3, d.keys()
returns a dynamic view of the keys in the dict
which results in the same problem. Instead, iterate over list(d)
as this will produce a list from the keys of the dictionary that will not change during iteration
add a comment |
Dictionary uses insertion order with an additional level of indirection, which causes hiccups when iterating while keys are removed and re-inserted, thereby changing the order and internal pointers of the dictionary.
And this problem is not fixed by iterating d.keys()
instead of d
, since in Python 3, d.keys()
returns a dynamic view of the keys in the dict
which results in the same problem. Instead, iterate over list(d)
as this will produce a list from the keys of the dictionary that will not change during iteration
add a comment |
Dictionary uses insertion order with an additional level of indirection, which causes hiccups when iterating while keys are removed and re-inserted, thereby changing the order and internal pointers of the dictionary.
And this problem is not fixed by iterating d.keys()
instead of d
, since in Python 3, d.keys()
returns a dynamic view of the keys in the dict
which results in the same problem. Instead, iterate over list(d)
as this will produce a list from the keys of the dictionary that will not change during iteration
Dictionary uses insertion order with an additional level of indirection, which causes hiccups when iterating while keys are removed and re-inserted, thereby changing the order and internal pointers of the dictionary.
And this problem is not fixed by iterating d.keys()
instead of d
, since in Python 3, d.keys()
returns a dynamic view of the keys in the dict
which results in the same problem. Instead, iterate over list(d)
as this will produce a list from the keys of the dictionary that will not change during iteration
answered Apr 4 '18 at 9:48
AB AbhiAB Abhi
991821
991821
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.
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%2f49646583%2fmodify-list-and-dictionary-during-iteration-why-does-it-fail-on-dict%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
2
if it could be implemented for lists, that would certainly be a life-saver.
– Jean-François Fabre
Apr 4 '18 at 8:51
1
This is something I’ve been curious about for a while too. The behaviour is documented but not justified at docs.python.org/3/reference/compound_stmts.html#for. Might just be for *handwave* performance reasons? Or a conservative choice from one point of view – allowing the operation because it can be allowed, whereas it would never make sense on dicts pre-3.6 given that they were unordered.
– Ry-♦
Apr 4 '18 at 8:52
1
I could think of cases where you would want to add to a list while iterating over it, somewhat like a queue.
– phogl
Apr 4 '18 at 8:53
2
looking at dict source code iterating on keys doesn't seem trivial, like at all.
– Jean-François Fabre
Apr 4 '18 at 8:56
it's the same behaviour for
set
. I guess it has to do with consistency tables hashing vs values. lists don't have that complexity.– Jean-François Fabre
Apr 4 '18 at 8:57