Mutable Default Parameter Values
00:00 Let’s take a look at what can happen if you try to use a mutable object as a default parameter value in Python. This is a very classic example to demonstrate what’s happening with default values and mutable objects.
00:16
I have a function that’s going to append a string of three hashtags ('###'
) to whatever list is passed as a parameter. And if it doesn’t have a parameter, then an empty list is created and the hashtag string is appended to that.
00:32
Let’s go ahead and take a look at what’s happening here. I’ve put this in a file called function_mut_def
for “function mutable default.” It’s the same function we saw on that previous slide.
00:45
Let’s go ahead and import it and use it! So, from function_mut_def
, let’s import our function. And if I provide a list to this function,
01:05
['foo', 'bar', 'baz']
, we have provided a list and so the function f()
is going to take that list and append that string of hashtags to the end of it.
01:17
It doesn’t particularly care what the list looks like. If I give it a list of numbers, f()
says, “Okay! I’ll append the string to the end of that list.” And if I don’t provide a list, f()
is going to take its default value of an empty list and append the '###'
string to it.
01:39
And everything seems to be working just the way we expected until we call f()
without an argument a second time. We were expecting f()
to take an empty list and append the '###'
string to it, but that’s not what happened.
01:58 It took the previous list and keeps appending hashtags to that. Well, why is that? And how do we fix this to behave the way we want it to?
02:13 So, what’s happening here? In Python, the default value for a parameter value is defined only once. So when we imported that module and that function, Python executed it in a way that simply identified the name of the function, the name of the parameter, and its default value, and then it read the rest of the body of that function.
02:40
In other words, when we were at this point after the import
statement, the parameter my_list
already had the empty list value. If we provided a list, that argument replaced this one.
03:00 However, if we didn’t, then it continued to use the list that had been defined when the function’s definition statement was first executed.
03:13
So, the default value isn’t redefined. It takes that first definition and continues to use it. Every time we call f()
without a parameter value, we’re performing .append()
on that same list.
03:27 So, what’s a better way to write that function to use a new list each time?
03:36 And again, this is a well-known problem with a well-known solution. Like I said, you can probably search Real Python and find this particular example in other courses and in other articles.
03:49
The solution is to set my_list
to have a default value of None
. Now, None
is not a mutable object, and so we aren’t going to be changing it each time.
04:03
Each time we call it without a parameter, we just have None
. So we make a check. If my_list
is None
, then we create a new list and then we append to that.
04:17
And so this particular version has the behavior that we wanted. I called this function_mut_def2
. Brilliant name, I know. Let’s quit this and restart our REPL.
04:37
And this is from function_mut_def2 import f
.
04:44 If we provide an actual list,
04:51
we will append the '###'
string to that list. If I don’t provide a list, then my_list
, the parameter, still has a value of None
, since that’s the default value to use.
05:08
And each time the default value is None
, then we create a new list and then we append to whatever that list is. The point to make is that my_list
isn’t defined to a list at the definition execution.
05:26
my_list
is set to None
as the default value, and that’s not mutable, so every time we call it without an argument, it comes back to this value of None
.
05:36 It doesn’t have a list at this point to use. And so we can actually see that this does have the behavior that we want.
05:47
So again, if no argument is provided, my_list
has a value of None
. The if
statement then causes a new empty list to be created.
05:57
And we saw that if we do provide an argument, then my_list
is assigned to use that, and that list is modified appropriately.
06:07 The next few lessons are going to talk about how Python passes objects to a function and what you’re able to do with them, and compare it to other languages and how they choose to modify or not modify a parameter value that’s given to it.
TheArchon on Dec. 14, 2023
The tutorial executes print(f()) twice in a REPL.
Add a second print(f()) to the code above and execute it as a file to get the behavior demonstrated in the tutorial.
Become a Member to join the conversation.
Brandon Hopkins on April 21, 2023
Everytime I call the empty function, it returns a new list
[###]
, rather than adds to it. Perhaps Python has fixed this? No need to putmy_list = None
anymore?The above example did exactly the same as
my_list = None
, etc.