Before definitively stating the question I would like you to take into account the documentation of python 4.7.1 <-> 4.7.2 specifically in the Important Notice .
In this they clearly establish that when the arguments are named and at the same time they are mutable, they will maintain their values between calls to the function, because the question is, then:
Why?
Why would these keep their values, if all the variables used in a function are supposed to be destroyed once the function is finished?
I came to assume that when a function with named arguments is invoked (without passing arguments to it in its clear place) these variables are declared globally, and, unless they are declared globally but are inaccessible, this is not true...
You're messing up some concepts. The documentation warning does not refer to values passed by name, but to parameters with default values . I copy the relevant paragraph:
And in fact, here's an example:
So that you understand what happens here, suffice it to say that the previous code is equivalent in behavior to this other one:
In this second form it is clearly seen that there is an external object to the function, which is an empty list. The default value of the parameter
L
is set to be that external list. And that list will be empty the first time you call the function. But since the function modifies the list, adding an element, at the return of the function that global list will have one more element:The list grows, which makes sense because it is global, and because the parameter
L
, when we do not give it another value, is pointing to that external global variable.The surprise is that the same thing happens when you declare it like this:
And this is surprising simply because you would expect the assignment to
L=[]
create an empty list every time you call the function, but it doesn't. The empty list is created before the function is called, and is external to it, like in the second example, only anonymous. That is, your intuition that it was "global" is basically correct. But not the variableL
, which is internal to the function and dies when the function ends, but the list it is initialized withL
if you don't pass it another. It is always the same list that exists outside the function.If you want the function to actually use a new empty list every time you call it with no arguments, the correct way would be:
And now there are no surprises:
Note on mutability
Why does the documentation warning expressly mention mutability?
It turns out that in Python all variables are actually object references . This is also true for function parameters (a reference to the object being passed as an argument is copied to the parameter). And also for parameters with default values (they are initialized with references to objects that are the default values).
The warning that " The default value is evaluated only once " is valid for any Python data type, only it is conflicting if the type is mutable. And the reason is simple. Since the function is actually receiving a reference to an external object, if that reference is used from within to mutate the object, that mutation will be stored in the object permanently.
Mutating the object consists of using one of its methods to change it. For example if it
L
is a list, useL.append()
modifies the object itL
points to. Or also useL[0] = 3
would modify it.Instead, assigning a new value is not mutating the object. That is, if inside the function we do
L = [1,2,3]
, that has not mutated the external object. What has happened is simply that the referenceL
has stopped pointing to that external object, to point to a newly created list. The outer object remains with its original value.If the data that we pass as a default value to
L
is not mutable (for example an integer), there is no chance that we will observe the mysterious side effects of mutating the object. An integer is not mutable because it has no methods to change it. For example, there is no hypotheticalint.increment()
that changes a 1 for a 2. And if instead the function does something likeL=L+1
(beingL
an integer, for example with the value 1), nothing would be mutating for the reasons explained above, it would only be changing the reference that pointed to a 1 to point to a 2.Another example
A class you create is also mutable, because once you have a reference to an object (say
x
) you can do things likex.atributo=valor
. So in functions that receive an instance of a class as a default parameter, the same thing will happen as with lists or dictionaries. If the function modifies attributes of that object, the modifications are persistent between calls:The creation of the object
Clase()
that is assigned as the default value occurs only once, and not on each call.Naturally there are no problems if you pass it a value instead of using the default value: