What I’m trying to do:
Understand the behavior of default params in python classes. I made a sort of random change to the class init, which achieves the behavior I want, but I don’t understand why it does.
What I’ve tried and what’s not working:
Originally in the Week Custom component the init looked like:
def __init__(self, week_number, days = [], **properties):
self.days = days
self.week_number = week_number
self.init_components(**properties)
When printing the length of the days variable for any instance of Week, I always got back the total number of days, which was 30.
This gave me the expected behavior for printing the number of days in the week, which means there’s something I don’t understand about the init method, and default values, changing variables to act like instance variables, rather than class variables.
One thing that I noticed immediately is that you used a mutable as a default argument, which is sometimes dangerous.
You can google it to get more details, but the quick summary is that if days is changed inside the function, something like days.append('Thursday'), the default argument from now on is ['Thursday'] instead of [].
Now I keep reading your post and I will get back to you if I have more to say
EDIT
I read the rest of the post (I haven’t cloned and tested), but I’m guessing that was the problem.
When you want to assign an empty list or dictionary as default value to an argument, you should do something like this:
def __init__(self, week_number, days=None, **properties):
if days is None:
days = []
ahh yes, that is indeed the problem. Looking at an article here on it, but it doesn’t really explain what actually happens underneath the hood, only gives an example of what a functionally equivalent piece of code does.
Edit:
This post from SO offers a better explanation.
" Actually, this is not a design flaw, and it is not because of internals or performance.
It comes simply from the fact that functions in Python are first-class objects, and not only a piece of code.
As soon as you think of it this way, then it completely makes sense: a function is an object being evaluated on its definition; default parameters are kind of “member data” and therefore their state may change from one call to the other - exactly as in any other object.
In any case, Effbot has a very nice explanation of the reasons for this behavior in Default Parameter Values in Python.
I found it very clear, and I really suggest reading it for a better knowledge of how function objects work."
My understanding is that the interpreter allocates a variable with the value of each optional argument (the arguments that have a default value defined in the function signature).
Later, when the function is called without the value for the optional argument, the interpreter creates a variable in the function scope with that default value.
If the value is an immutable, like an int or an str, then the value is copied to the new variable. And if the function changes it, then a new variable is created every time, so the default value doesn’t change. (Yes, python doesn’t change the value of an immutable variable, it creates a new one every time you change its value.)
If the value is a mutable, like a list or dict, then the local variable is as new as it is with immutables, but it points to the mutable list. If you then use that new local variable to modify the mutable it points to, then you mutate the variable used to store the default value.