Home Python Course #12: Pass by Assignment, Copy, Reference, and None
Post
Cancel

Python Course #12: Pass by Assignment, Copy, Reference, and None

Now that the you know what mutable (e.g. lists, sets and dictionaries) and immutable data types (e.g. tuples) are it is important to talk about copies, references and None.

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 (bool, int, float, and 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. list, set, 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 int i and the list l are declared and then passed to func(x, y). When looking at i and l after 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 .copy() function:

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']

By using .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 list ['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. l and m reference the first list and n reference the second list. Because l and m reference the same list every modification done using l or m will always be reflected in both. n won’t change as it references a different list.

Python is vs ==

To check if two variables reference the same value you can use the is operator to compare the values use the == operator:

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 id(...):

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 id of l and m is the same and the id of n is different (The numbers are different everytime you run this code). The is operator is actually implemented using == with id(...) == id(...).

Python None

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 Cheat Sheets in my Gumroad shop. If you have any questions about this article, feel free to join our Discord community to ask them over there.

This post is licensed under CC BY 4.0 by the author.