satwikkansal/wtfpython

Output of "Modifying a dictionary while iterating over it" is not correct for all Python versions

icemac opened this issue · 3 comments

The result shown in Modifying a dictionary while iterating over it is correct for CPython 2.4 up to 3.5.

On Python 3.6 it prints

0
1
2
3
4

On PyPy 2.7 and PyPy 3 it prints

0

For Python 3.6.4 on macOS 10.12:

Python 3.6.4 (default, Dec 20 2017, 18:41:55)
Type 'copyright', 'credits' or 'license' for more information
IPython 6.2.1 -- An enhanced Interactive Python. Type '?' for help.

In [1]: x = {0: None}

In [2]: for i in x:
   ...:     del x[i]
   ...:     x[i+1] = None
   ...:     print(i)
   ...:
0
1
2
3
4

In [3]:

Hey @icemac, thanks for reporting this. I'll investigate the reason for different behavior in the latest versions of Python.

Okay, I dug up a little and found these differences in the implementation between Python 3.5 and 3.6 which explains the behavior.

In both the CPython versions, the hash table for dictionary starts with a size of 8 (Python 3.6.1 and Python 3.5.1). The resize occurs when the table size exceeds the USABLE_FRACTION which is different for both the Python versions ((2n/3 for Python 3.6.1 and (2n+1)/3 for Python 3.5.1.

Moreover, the way deleted keys are handled are probably different in both the Python version (I'm still trying to understand how they work), which leads to all the difference.

I think this is highly implementation specific, so I've added a note in the example regarding the Python versions for now, and I'll link this issue in the example description (for further reading of interested readers) once we figure out how deletions are handled in both the versions of python.