2

I'm coming from C, learning Python. In Python (3.5.2), it seems that assignment of one data type to another of the same type is sometimes done by value and sometimes by reference.

For example, strings are assigned by value:

>>> str1 = "hello"
>>> str2 = str1
>>> print(str1, str2)
hello hello
>>> str2 = "goodbye"
>>> print(str1, str2)
hello goodbye

This is the behavior I expect. However, it works differently with dictionaries:

>>> dict1 = {'key1':'val1', 'key2':'val2'}
>>> dict2 = dict1
>>> print(dict1, dict2)
{'key2': 'val2', 'key1': 'val1'} {'key2': 'val2', 'key1': 'val1'}
>>> dict2['key1'] = 'newval'
>>> print(dict1, dict2)
{'key2': 'val2', 'key1': 'newval'} {'key2': 'val2', 'key1': 'newval'}

Notice that both dict1 and dict2 were changed. Similarly, if I add a key/value pair to either of the dictionaries it will appear in both. Argh!

(sorry, that was my C-background speaking) :)

How am I to know which behavior to expect for any given variable type? Is there a method to this madness or will I simply need to remember arbitrary rules?

P.S. I realize I can get my expected behavior by using dict2 = dict(dict1).


The "possible duplicate" contains good info on how to do it, but I'm interested in why I have to. The answers to this question are already very helpful!

bitsmack
  • 1,324
  • 3
  • 21
  • 31
  • 3
    Possible duplicate of [How to copy a dictionary and only edit the copy](https://stackoverflow.com/questions/2465921/how-to-copy-a-dictionary-and-only-edit-the-copy) – idjaw Jun 13 '17 at 01:16
  • to copy a dictionary use new_dict = dict(old_dict) otherwise they will reference the same dict. – syntaxError Jun 13 '17 at 01:17
  • There's a difference between `var = ...` and `var[...] = ...`. The first is reassigning the variable to point to a different object (as is the case with `str2 = "goodbye"`). The second is an action on the object: `dict2.__setitem__('key1', 'newval')`. – zondo Jun 13 '17 at 01:20
  • 4
    Have a quick [guide](https://nedbatchelder.com/text/names.html) to how assignment and variables work in Python. Neither "by value" nor "by reference" is an accurate description. – user2357112 Jun 13 '17 at 01:26
  • 1
    For a brief summary (with nice diagrams) of the key information in the link user2357112 posted, please see [Other languages have "variables", Python has "names"](http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html#other-languages-have-variables). – PM 2Ring Jun 13 '17 at 01:36

2 Answers2

5

In Python, everything is by reference; This is different from C, variable is not a box. The assignment is actually a binding.

str1 = "hello"
str2 = str1

Both str1 and str2 reference to "hello", but string in Python is immutable, so modifying str2, would creating a new binding, and hence do not affect the value of str1

str2 = "hack"
# here str1 will still reference to "hello" object

Dict works the same:

d1 = {"name": "Tom"}
d2 = d1

d2 and d1 will reference the same object. If change d2 to a new value do not affect the value of d1; But instead, if we only modify d2 in place, like d2['age'] = 20, the value of d1 would be changed since they share the same object.

d2 = {"name": "Hack"}
# the value of d1 does not change

Luciano Ramalho summarized it in Fluent Python, Chapter 8, Variables Are Not Boxes section

To understand an assignment in Python, always read the righthand side first: that’s where the object is created or retrieved. After that, the variable on the left is bound to the object, like a label stuck to it. Just forget about the boxes.

Jacky1205
  • 3,273
  • 3
  • 22
  • 44
  • 2
    `but string in Python is immutable, so modify str2 (create a new binding) will not affect the value of str1`. This is not a sensible conclusion. For instance, bytearray is mutable, but assigning a bytearray to `str2` would still not mutate the old value. @bitsmack is assuming that assignment does something, like copying values in C. That is not the case. – phihag Jun 13 '17 at 01:32
  • 2
    @phihag: Bytestrings are also immutable; maybe you meant bytearrays or something. – user2357112 Jun 13 '17 at 01:33
  • Thank you! This explanation and the examples are just what I was looking for. Yay! Not arbitrary :) – bitsmack Jun 13 '17 at 17:55
2

You are misinterpreting how assignments work. In Python, assignment (=) sets the name on the left side to the value on the right side.

This is roughly equivalent to assigning pointers in C, where the same behavior exists:

char* str1 = "hello";
char* str2 = str1;
str2 = "world";
printf("%s %s", str1, str2); // prints "hello world"

If you want to create a copy of a value in Python, call a function that does that or use a comprehension to define a new version. For instance, to copy a dictionary, the following work:

dict2 = dict(dict1)          # dict constructor (shallow copy)
dict2 = copy.copy(dict1)     # shallow copy
dict2 = copy.deepcopy(dict1) # deep copy
dict2 = {k: v for k, v in dict1.items()} # dict comprehension

# or, in multiple lines ...
dict2 = {} # literal: create a brand-new dict, equivalent to dict()
for k, v in dict1.items():
    dict2[k] = v
phihag
  • 278,196
  • 72
  • 453
  • 469