Reputation: 65
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
Reputation: 78740
x = something
, x
is a new name for the value of something
.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
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