This article describes the potential hazards of using the Mutable object as the default value for the python function parameter, and its implementation principle and design purpose
Trap Recurrence
We will use practical examples to illustrate the main topics we are going to discuss today.
The following section of code defines a function named Generate_new_list_with. The function is intended to create a new list with the given element value each time it is called. The actual operation results are as follows:
Python 2.7.9 (default, Dec, 06:05:48) [GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.56)] on Darwintype ' help ', "Copyright", "credits" or "license" for more information.>>> def Generate_new_list_with (my_list=[], element= None): ..... My_list.append (Element) ... return my_list...>>> list_1 = Generate_new_list_with (element=1) >>> list_1[1]>>> list_2 = Generate_new_list_with (element=2) >>> list_2[1, 2]>>>
The visible code does not run as we expected. List_2 does not get a new list at the second call of the function and fills in 2, but append a 2 on the basis of the first call to the result. Why does this happen? In other programming languages, it's just like designing bugs.
Preparation knowledge: The nature of Python variables
To understand the cause of this problem, we need a readiness knowledge, that is: How exactly is the python variable implemented?
Python variables differ from the Declaration & assignment methods of other programming languages, and are implemented by creating & pointing to a pointer-like manner. That is, a variable in Python is actually a pointer to a value or an object (simply saying they are worth a name). Let's look at an example.
p = 1p = P+1
For the traditional language, the above code will be executed in the memory to declare a variable p, and then put 1 in the memory of the variable p. When the addition operation is performed, a 2 result is obtained, and the value of 2 is deposited again in the memory address of P. Visible in the entire execution process, the change is the value of the memory address where the variable p is located
In this piece of code, Python actually creates a 1 object in memory now and points p to it. When the addition operation is performed, a new object of 2 is actually obtained through the addition operation, and P points to the new object. Visible in the entire execution process, the change is the memory address that P points to
Root cause of the default value trap for function parameters
One sentence to explain: The parameter default value of the Python function is bound at compile time.
Now, let's go through an excerpt to analyze the cause of the trap in detail. Here is an explanation of the reason for the excerpt from Python Common gotchas:
Python's default arguments was evaluated once when the function was defined, not each time the function was called (like it Is in say, Ruby). This means the if you use a mutable the default argument and mutate it, you'll and has mutated that object for all the future Calls to the function as well.
Visible if the parameter default value is determined at the function compilation compile stage. After all the function calls, if the argument does not show the given assignment, then the so-called parameter default value is simply a pointer to the object that already existed at the compile stage. If the function is called, the specified incoming parameter does not appear to be worth saying. In all cases, this parameter will exist as an alias for the object that was created at compile time.
If the default value of the parameter is an immutable (imuttable) value, if the parameter is modified in the body of the function, then the parameter is re-pointing to another new immutable value. And if the default value of the parameter is the same as the one at the beginning of this article, it is a mutable object (muttable), then the situation is worse. The modification of this parameter in all function bodies is, in fact, a modification of the object that has been determined at the compile stage.
There are also special hints for such a trap in the official Python documentation:
Important warning:the Default value is evaluated only once. This makes a difference when the default was a mutable object such as a list, dictionary, or instances of most classes. For example, the following function accumulates the arguments passed to it on subsequent calls:
How to avoid this trap brings no need to trouble
Of course, the best way is not to use mutable objects as default values for functions. If this is not the case, here's a solution. Take the example of the need for the beginning of the article:
def generate_new_list_with (My_list=none, Element=none): if My_list is None: my_list = [] my_list.append ( Element) return my_list
Why is python designed like this?
The answer to this question can be found on StackOverflow. The most important part of the answer that will have the most votes is the following excerpt:
Actually, this was not a design flaw, and it was not because of internals, or performance.
It comes simply from the fact that functions in Python is first-class objects, and not only a piece of code.
As soon as you get to think into the this and then it completely makes Sense:a function are an object being evaluated in its Definition Default parameters is kind of "member data" and therefore their state could change from one call to the other–exactly as In any other object.
In any case, Effbot have 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 what function objects work.
In this answer, the speaker thinks that the function is an internal-level object, considering the implementation of the Python compiler. The default value of the parameter is the property of the object. In any other language, object properties are bound when the object is created. Therefore, it is not surprising that the default value of a function parameter is bound at compile time.
However, there are many other respondents who do not buy it, and think that even first-class object can be bound at execution time using the closure method.
This is a design flaw. It is a design decision; Perhaps a bad one and not a accident. The state thing are just like any and closure:a closure are not a function, and a function with mutable default argument is not a function.
There is even a rebuttal of the realization of the logic, simply from the design point of view: As long as it is against the procedural ape basic thinking logic behavior, are design flaws! Here are some of their arguments:
> Sorry, but anything considered ' the biggest WTF in Python ' is most definitely a design flaw. This was a source of bugs for everyone at some point, because no one expects that behavior at First–which means it should Not there are been designed that, that is, to begin with.
The phrases "This isn't generally what was intended" and "A-around this is" smell like they ' re documenting a design f Law.
Well, the answer to this question will always be a mystery, if it doesn't come from a python author's own Chen Ching.