Zubair Azami
Zubair Azami

Reputation: 65

Does dictionary assignment results in pointing to the same object in Python?

While implementing some design patterns in Python, I found something interesting about Dictionaries that seems unusual. I created a variable dict1 with dict constructor. Then dict1 was assigned to dict2. But while changing a value of a key in dict2, it affected dict1.

Code:

dict1 = dict(a=1, b=2)
dict2 = dict1
print("dict1 = ", dict1)
print("dict2 = ", dict2)

# assigning value of key 'a' in dict2 only
dict2['a'] = 0
print("---------------------------")
print("dict1 = ", dict1)
print("dict2 = ", dict2)

Output:

dict1 =  {'b': 2, 'a': 1}
dict2 =  {'b': 2, 'a': 1}
---------------------------
dict1 =  {'b': 2, 'a': 0}
dict2 =  {'b': 2, 'a': 0}

I found the same type of behavior for Lists also:

list1 = [1, 2, 3]
list2 = list1
print(list1, list2)
print("---------------------------")
list2.append(4)
print(list1, list2)

Output:

[1, 2, 3] [1, 2, 3]
---------------------------
[1, 2, 3, 4] [1, 2, 3, 4]

But not in strings:

str1 = "Good"
str2 = str1
print(str1, ",", str2)
print("---------------------------")
str2+=" thing"
print(str1, ",", str2)

Output:

Good , Good
---------------------------
Good , Good thing

It is obvious that dictionary & list assignment differs from string assignment. But how does it work? I was looking for some explanation or related resources. Can anyone help me out ? Thanks in advance.

Upvotes: 2

Views: 171

Answers (2)

timgeb
timgeb

Reputation: 78740

  1. Python knows names and values.
  2. When you do x = something, x is a new name for the value of something.
  3. Assignment never copies data.

With that in mind, let's go over the two examples:

When you do

dict2 = dict1

dict2 is a new name for the dictionary {'a':1, 'b':2} which also has the name dict1. All changes to the dictionary will be visible across all names!

When you do

str2 += " thing"

you are rebinding the name str2 to the result of str2 + " thing". You are building an entirely new string, and rebind the name str2. str1 and str2 are now names for different strings and str1 is still pointing to the original string.

One last thing/pitfall to keep in mind: sometimes, the += operator can trick you and x += y will do something else than x = x + y.

Here's an example:

>>> x = [1, 2, 3]
>>> y = x
>>> x = x + [4, 5, 6]
>>> x
[1, 2, 3, 4, 5, 6]
>>> y
[1, 2, 3]

As expected, when you do x = x + [4, 5, 6], you build a new list and rebind the name x. y is still pointing to the old list. However...

>>> x = [1, 2, 3]
>>> y = x
>>> x += [4, 5, 6]
>>> x
[1, 2, 3, 4, 5, 6]
>>> y
[1, 2, 3, 4, 5, 6]

This is unexpected, since conceptually

x += [4, 5, 6] 

should do the same as

x = x + [4, 5, 6]

What happens is that when you use += you are calling __iadd__ on x, i.e. what happens is x.__iadd__([4, 5, 6]).

In the case of lists, __iadd__ extends the list in place instead of building a new list object, so x += [4, 5, 6] was equivalent to

x.extend([4, 5, 6]) 

and then returning x. Strings behave as expected when using +=, i.e. a new string is built and you reassign the name.

Upvotes: 5

Daniel Roseman
Daniel Roseman

Reputation: 599648

The answer to the question posed in the title is "yes". But this is not specific to dicts and lists - it is exactly the same for any object.

Neither is it true that assignment differs from strings. The only difference is that dicts and lists allow you to mutate them; you can see that your code does different things in each case - append for a list and key assignment for a dict, but += for a string. These are different operations, so it shouldn't surprise you that they have different behaviours.

Upvotes: 3

Related Questions