3

I cannot rename the Python dict using pop as described below. I've used suggestions from this post. Is this a Python bug? I'm using Python 3.5.2

>>> d = {'a': 'ABC', 'b': 'CDE', 'c': 'KRT'}
>>> for old_key in d.keys():
        d['NEW_KEY_' + old_key] = d.pop(old_key)
>>> d

{'NEW_KEY_NEW_KEY_NEW_KEY_b': 'CDE', 'NEW_KEY_NEW_KEY_a': 'ABC', 'c': 'KRT'}
honza_p
  • 2,073
  • 1
  • 23
  • 37
HLopes
  • 155
  • 5
  • I don't understand what you think is buggy behaviour here? – roganjosh Aug 30 '18 at 10:41
  • @roganjosh presumabley the fact that `'NEW_KEY_NEW_KEY_NEW_KEY_b'` exists as does `'c'` – FHTMitchell Aug 30 '18 at 10:42
  • @FHTMitchell You're probably right. It's still not buggy, it's just more fuel for the argument of not changing something while iterating over it :) – roganjosh Aug 30 '18 at 10:44
  • For what it's worth, I'm unable to reproduce this in a Docker `python:3.5.2` container (it is reproable in `3.4.9`). – AKX Aug 30 '18 at 10:45

3 Answers3

2

In Python 3, you have to change for old_key in d.keys() into for old_key in list(d.keys()). That should work because you are iterating over a dynamic entity.

honza_p
  • 2,073
  • 1
  • 23
  • 37
  • This needs a bit more explanation IMO. How `dict.keys()` is a view and why you shouldn't mutate something you are iterating over. – FHTMitchell Aug 30 '18 at 10:44
  • You are welcome to update the answer. Ironically, the code posted in the question works for me already ;-) – honza_p Aug 30 '18 at 10:45
1

It's mentioned in the documentation that changing a dictionary while iterating over it might fail to produce correct results. You'd better write something like

new_dict = {'NEW_KEY_' + key: value for key, value in old_dict.items()}
blue_note
  • 27,712
  • 9
  • 72
  • 90
1

You are changing your keys as you iterate over a view of them. That's not recommended.

Instead, you can take a copy of your keys and iterate over them. Since dictionary keys are unique, you can use list or set:

d = {'a': 'ABC', 'b': 'CDE', 'c': 'KRT'}

for old_key in list(d):
    d['NEW_KEY_' + old_key] = d.pop(old_key)

# {'NEW_KEY_a': 'ABC', 'NEW_KEY_b': 'CDE', 'NEW_KEY_c': 'KRT'}

You can, of course, use a dictionary comprehension, which you should find more efficient.

jpp
  • 159,742
  • 34
  • 281
  • 339
  • @roganjosh, Don't see why not. Do you see a problem with this? The important thing is you aren't iterating over a view. – jpp Aug 30 '18 at 10:52
  • So the dictionary comprehension is the *recommended* method. But not everyone is comfortable with them. Where performance isn't a concern a regular `for` loop with `pop` is entirely fine. – jpp Aug 30 '18 at 10:54