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()?










24















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?










share|improve this question



















  • 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















24















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?










share|improve this question



















  • 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













24












24








24


2






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?










share|improve this question
















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






share|improve this question















share|improve this question













share|improve this question




share|improve this question








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 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












  • 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







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












3 Answers
3






active

oldest

votes


















26














I think the reason is simple. lists are ordered, dicts (prior to Python 3.6/3.7) and sets are not. So modifying a lists 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 dicts and sets 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 dicts 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.






share|improve this answer

























  • 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


















6














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.






share|improve this answer























  • 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


















2














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






share|improve this answer






















    Your Answer






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

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

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

    else
    createEditor();

    );

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



    );













    draft saved

    draft discarded


















    StackExchange.ready(
    function ()
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%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









    26














    I think the reason is simple. lists are ordered, dicts (prior to Python 3.6/3.7) and sets are not. So modifying a lists 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 dicts and sets 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 dicts 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.






    share|improve this answer

























    • 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















    26














    I think the reason is simple. lists are ordered, dicts (prior to Python 3.6/3.7) and sets are not. So modifying a lists 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 dicts and sets 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 dicts 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.






    share|improve this answer

























    • 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













    26












    26








    26







    I think the reason is simple. lists are ordered, dicts (prior to Python 3.6/3.7) and sets are not. So modifying a lists 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 dicts and sets 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 dicts 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.






    share|improve this answer















    I think the reason is simple. lists are ordered, dicts (prior to Python 3.6/3.7) and sets are not. So modifying a lists 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 dicts and sets 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 dicts 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.







    share|improve this answer














    share|improve this answer



    share|improve this answer








    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

















    • 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













    6














    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.






    share|improve this answer























    • 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















    6














    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.






    share|improve this answer























    • 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













    6












    6








    6







    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.






    share|improve this answer













    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.







    share|improve this answer












    share|improve this answer



    share|improve this answer










    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

















    • 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











    2














    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






    share|improve this answer



























      2














      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






      share|improve this answer

























        2












        2








        2







        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






        share|improve this answer













        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







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Apr 4 '18 at 9:48









        AB AbhiAB Abhi

        991821




        991821



























            draft saved

            draft discarded
















































            Thanks for contributing an answer to Stack Overflow!


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

            But avoid


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

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

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




            draft saved


            draft discarded














            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





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            1928 у кіно

            Захаров Федір Захарович

            Ель Греко