Now that the you know what mutable (e.g.
sets and dictionaries) and immutable data types (e.g.
tuples) are it is important to talk about copies, references and
Python Pass by Assignment
When you call a pass data to a function such as
print(...) there are two ways. The first option is to pass the data directly as a value:
1 2 >>> print(1, ['a', 'b']) 1 ['a', 'b']
The second option is to pass the data as a variable:
1 2 3 4 >>> i = 1 >>> l = ['a', 'b'] >>> print(i, l) 1 ['a', 'b']
The difference between those two options is that the second option allows you to access the data you passed to the function later in your program by stating the variable. When the function that receives the data modifies it internally, you have to be aware of two critical things:
When a primitive data type (
str) or immutable data type (e.g.
tuple) is passed to a function, its value is copied. Therefore, all changes made inside a function won’t affect the values stored in the variables passed to the function. This is called pass by value.
When a mutable data type (e.g.
dict) is passed to a function, the data isn’t copied. Because of this, changes that are made inside the function affect the values outside of the function. This is called pass by reference
This sounds pretty theoretical, so here comes an example:
1 2 3 4 5 6 7 8 9 10 11 12 def func(x, y): x = x - 1 y.pop() if __name__ == "__main__": i = 1 l = ['a', 'b'] func(i, l) print(i, l) # Output: 1, ['a']
Even though you haven’t learned about declaring functions in Python this example declares the function
func(x, y) that takes two parameters.
func(x, y) subtracts 1 from the first parameter, and calls
pop() on the second one. That is everything you need to understand at the moment.
In the main program, the
i and the
l are declared and then passed to
func(x, y). When looking at
func(x, y) has been executed, you can see that
i is still 1 and not 0 because
i’s value was copied. However,
l is missing its last element since it was passed as a reference.
This mix of pass by value and pass by reference in Python is called pass by assignment. It is essential to keep this concept always in mind when writing Python code.
In the previous parts of this Python course, you have seen that it is possible to get a copy of a mutable data type by calling the
1 2 3 4 5 6 7 8 9 10 11 12 def func(x, y): x = x - 1 y.pop() if __name__ == "__main__": i = 1 l = ['a', 'b'] func(i, l.copy()) print(i, l) # Output: 1, ['a', 'b']
.copy(), the data outside of the function isn’t changed.
In the following example the difference between a copy and reference is further amplified:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 >>> l = ['a', 'b', 'c'] >>> m = l >>> n = l.copy() >>> l.pop() 'c' >>> l ['a', 'b'] >>> m ['a', 'b'] >>> m.pop() 'b' >>> m ['a'] >>> l ['a'] >>> n ['a', 'b', 'c']
In the first line the list
['a', 'b', 'c'] is declared and than a new reference to this
list is created with
l =. In the second line another reference to the
['a', 'b', 'c'] is created with:
m = l. In the third line the
list is copied and a reference to that copy is created with:
n = l.copy().
There are three references to two
lists with the same content.
m reference the first
n reference the second
m reference the same
list every modification done using
m will always be reflected in both.
n won’t change as it references a different
Python is vs ==
To check if two variables reference the same value you can use the
is operator to compare the values use the
1 2 3 4 5 6 7 8 9 10 11 >>> l = ['a', 'b', 'c'] >>> m = l >>> n = l.copy() >>> l is m True >>> l is n False >>> l == m True >>> l == n True
l is n evaluates to
False because they reference differnt
lists, however, those two
lists contain the same values and therefore
l == n evaluates to
True. You can also check the
id that a variable is referencing using
1 2 3 4 5 6 7 8 9 >>> l = ['a', 'b', 'c'] >>> m = l >>> n = l.copy() >>> id(l) 139754651128128 >>> id(m) 139754651128128 >>> id(n) 139754650277952
You can see that the
m is the same and the
n is different (The numbers are different everytime you run this code). The
is operator is actually implemented using
id(...) == id(...).
Now that you know the difference between a copy and a reference, there is one last thing to talk about: ` None
. The keyword None
in Python indicates no value at all. And there is only one None`:
1 2 3 4 5 6 7 8 >>> a = None >>> b = None >>> a is b True >>> id(a) 93869826576896 >>> id(b) 93869826576896
None can be used to initialize a variable without assigning a value. This can be useful when the value of a variable is assigned under a condition:
1 2 3 4 5 6 7 if __name__ == "__main__": x = None b = 42 if b > 23: x = 13 print(x)
None actually has it’s own type and is an immutable data type because it can’t be changed:
1 2 >>> type(None) <class 'NoneType'>
None concludes this article. Throughout this Python course, you will come across pass by assignment, copies, references, and
None reasonably often. Make sure to get the free Python Sets Cheat Sheet in my Gumroad shop. If you have any questions about this article, feel free to join our Discord community to ask them over there.