Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Avoiding Side Effects

00:00 Now that you know how to use the return statement, let’s look at side effects again and see how a Python program would do some of the actions that we would perform using side effects in other languages.

00:13 For example, suppose that you want to write a function that takes a number and doubles that number. In C++, it would look something like this. We would use pass-by-reference and it would take that object, that variable, and it would actually double its value.

00:32 We don’t need a return statement because we are actually modifying the argument that was provided.

00:40 Let’s take a look at this. I’ve used more compact notation, x *= 2, same effect. So here, we’re going to declare a variable 5 and here is that weird-looking output statement.

00:53 We’re going to print out its value before we call this function. And then again, we’re going to call the function, which is going to take this x and double it.

01:04 This name x, although in a different scope, is actually referring to the same x that this is. It’s being doubled. This is pass-by-reference, so we can see—or we will see after calling this function—it has been given a value of 10.

01:24 So, let me compile this.

01:34 And then just to see the result as expected,

01:38 the ampersand (&) I used up here indicates that we are passing by reference. The function has direct access to this variable x, and so when it changes x up here, that change is seen down here in the function. Python doesn’t have pass-by-reference, and so if we tried writing that as a function…

02:15 At the beginning, we’ve already seen that x does refer to the same integer object 5, but when I reassign it, it forgets all about that object and creates a new object and has x refer to that. So in this environment, x is still 5. But what if that’s what you want?

02:35 You actually want x to have that new doubled value? Well, in Python, we would do two things. One, we would change our function.

02:48 Since there’s no point in modifying the value, we’ll just create a new value, which is twice the original value, and return it.

02:58 And now if we assign x equal to 5, the Pythonic way to make this happen is then to set x again back to the result of this function.

03:11 So if we want to double the value of x, knowing that in this instance we’re doing pass-by-value, the only thing that we can do is apply this function, which now returns the modified value, and then save that back into the variable where it started, so now it has the value of 10.

03:31 So, what you would want to do as a side effect using pass-by-reference in another language doesn’t work in Python. So you perform the calculation necessary and then return that value and then know when you are using this type of function to save the value returned back to the same variable. And that’s especially the case if you are using an immutable object.

04:00 But we do know that lists are mutable, So I could create a function called double_list(), and there are two versions of this, so I’ll call this double_list1(), where I take my list and then as I propagate through the list, I multiply each value by 2 and save it back to that same value in the list.

04:24 And because a list in Python is mutable, this will work. So let me come back here, and I called this module doubler1, so from doubler1, we’re going to import everything. That’s the only thing that’s there.

04:44 So I’m going to create a list, I’ll call it a.

04:52 This is double_list1(). It takes a list, so we’ll give it the list a. And if I display the result of a, now I can see that every element has been doubled.

05:04 But this doesn’t line up with how we like to do the other operations—this is a side effect—and we might choose to have our list doubler mimic better how the numeric doubler would work.

05:19 And so instead of modifying the parameter, creating side effects, maybe what we would prefer is to create a new list where each value in the new list is double the corresponding element in the previous list and then return it.

05:38 And so that’s the version in what I’ve called doubler2. I create a new list and as I propagate through the list, I append to it a new value formed by the current value of the original list multiplied by 2, and then I return it.

05:57 So this version doesn’t have the side effects that the previous one did and more lines up with versions where we would have to do this, where the argument wouldn’t be a mutable object.

06:12 And so in that context, if we’re not going to use side effects, then it’s going to look much more like the version we used at the top of the screen, where we are going to call the function.

06:28 I called this double_list2(). I say a is the parameter. Since a isn’t modified by this function, I need to save the return value back to a, and this would be the more common way to make this happen.

06:48 And so to avoid the use of side effects, when we want to modify an object in our function we should create a new object with the appropriate parameters, return that object, and then know in the calling environment just to save that back to the original variable.

07:07 Next will be a series of lessons on creating variable-length argument lists. A lot of fun stuff coming up!

Avatar image for Jonathan Chatfield

Jonathan Chatfield on Sept. 19, 2021

In my opinion, the digressions into C++ examples was more confusing, than clarifying.

Become a Member to join the conversation.