7

I am confused how python is iterating through this dictionary. From python's documentation, the itervalues returns an iterator over the dictionary's values.

dict = {"hello" : "wonderful", "today is" : "sunny", "more text" : "is always good"}

for x in dict.itervalues():
    x = x[2:]   

print dict

This prints out the original dictionary unchanged. Why is that? If I am saying the value at position x is "blabla", why is it not getting set?

Justin
  • 6,373
  • 9
  • 46
  • 72

3 Answers3

7

This has nothing to do with strings or lists. The devil is in how the for is unfolded.

Doing

for x in d.iteritems():
    # loop body

is more-or-less equivalent to doing

iter = d.itervalues()
while True:
    try:
        x = next(iter)
        # loop body
    except StopIteration:
        break

So with this in mind it's not very hard to see that we are just reassigning x, which holds a result from a function call.

iter = d.itervalues()
while True:
    try:
        x = next(iter)

        x = 5 # There is nothing in this line about changing the values of d
    except StopIteration:
        break
Emil Ivanov
  • 37,300
  • 12
  • 75
  • 90
  • Thanks, this has clarified it for me. I wish the python docs would explain things in more detail such as the intricacies of the for statement and various methods. – Justin Aug 10 '11 at 20:22
5

The only thing the line

x = x[2:]

does is creating the string slice x[2:] and rebinding the name x to point to this new string. It does not change the string x pointed to before. (Strings are immutable in Python, they can't be changed.)

To achieve what you actually want, you need to make the dictionary entry point to the new string object created by the slicing:

for k, v in my_dict.iteritems():
    my_dict[k] = v[2:] 
Emil Ivanov
  • 37,300
  • 12
  • 75
  • 90
Sven Marnach
  • 574,206
  • 118
  • 941
  • 841
  • 2
    This has nothing to do with strings being immutable; if I replace `"hello":"wonderful"` with `"hello":[1,2]` it works the same. – Mark Ransom Aug 10 '11 at 19:43
  • 1
    @Mark: I also gave the real reason of the behaviour -- the fact that strings are immutable is rather a side note. It does affect the solution though -- if values were lists, you could use `for x in my_dict: del x[:2]` to achieve the desired behaviour. – Sven Marnach Aug 10 '11 at 19:47
  • @Emil: I don't understand your comment. Is it related to my answer in any way? Or to my comment? Is this your downvote? – Sven Marnach Aug 10 '11 at 21:55
  • @Sven: Sorry, after reading it a few more times it's actually correct. – Emil Ivanov Aug 11 '11 at 05:13
1

As Sven Marnach points out, strings are immutable and you are just rebinding x to a new string created by the slice notation. You can demonstrate that x does point to the same object in the dictionary by using id:

>>> obj = 'hello'

>>> id(obj)
<<< 4318531232

>>> d = {'key': obj}   

>>> [id(v) for v in d.values()]
<<< [4318531232]

>>> [id(v) for v in d.itervalues()]
<<< [4318531232]

>>> [(k, id(v)) for k, v in d.items()]
<<< [('key', 4318531232)]

>>> [(k, id(v)) for k, v in d.iteritems()]
<<< [('key', 4318531232)]

You can use iteritems to iterate over key and value together to do what you want:

for k,v in dict.iteritems():
    dict[k] = v[2:]
Community
  • 1
  • 1
Zach Kelling
  • 52,505
  • 13
  • 109
  • 108