Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does python's dictionary iteration seemingly work with a copy?

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?

like image 749
Justin Avatar asked Aug 10 '11 19:08

Justin


3 Answers

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:] 
like image 26
Sven Marnach Avatar answered Oct 06 '22 00:10

Sven Marnach


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:]
like image 23
zeekay Avatar answered Oct 05 '22 23:10

zeekay


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
like image 112
Emil Ivanov Avatar answered Oct 05 '22 23:10

Emil Ivanov