Last time I shared an article about scheming Python variables and assignment, today I’ll pick up on this topic and talk about some of the pitfalls of assignment. Let’s start with a question:
>>> def func(numbers=[], num=1):
.numbers.append(num)
.return numbers
>>> func()
[1]
>>> func()
[1.1]
>>> func()
[1.1.1]Copy the code
We seem to have found a Bug where func() returns a different result each time we call it the same way, and the list is getting longer each time.
>>> id(func())
4330472840
>>> id(func())
4330472840Copy the code
As you can see from the above, the function returns the same list object because the id value is the same, but the elements in the list are changing. Why is that?
In Python, functions are the first class object. In other words, functions are objects that, like integers and strings, can be assigned to variables, passed as arguments, or returned as values. Functions also have their own attributes, such as the name of the function and the default parameter list of the function.
The name of the function
>>> func.__name__
'func'
A list of default arguments to the function
>>> func.__defaults__
([1.1.1.1.1].1)Copy the code
Def is an executable statement, the Python interpreter, when performing a def statement will be in memory is created a function object (at this point, the function does not perform the code inside logic, because haven’t call it), in the global namespace, there is a function name (variable called func) will refer to the function object, remember, to the beginning to end, No matter how many times the function is called, there is only one function object, which is the function object.
After the function object is generated, its properties: name and default argument list are initialized.
When initialization is complete, the first default argument in the __default__ attribute numbers points to an empty list.
When the function is called for the first time, the first time func() is executed, the logic inside the function (at which point the function no longer needs to be initialized) is to add an element of 1 to numbers
The second call to func() continues to add an element to numbers
The third, fourth, and so on.
So now you can see why calling the same function returns a different value each time. Because they share the same list (numbers) object, they just add one element to the list each time they are called
If we explicitly specify the numbers parameter, the results are completely different.
>>> func(numbers=[10.11[])10.11.1]Copy the code
Because numbers is reassigned, it no longer refers to the original initialized list, but to the new list object we passed, so the return value becomes [10, 11, 1].
So how can we avoid that? Do not use mutable objects as default values for arguments.
Correct way:
>>> def func(numbers=None, num=1):
.if numbers is None:
.numbers = [num]
.else:
.numbers.append(num)
.return numbers
...
>>> func()
[1]
>>> func()
[1]
>>> func()
[1]Copy the code
If the call does not specify an argument, the default argument numbers is reassigned each time the method is called, so numbers will refer to a new object each time. This is the difference with the former.
So, should we never use mutable objects as default values for arguments? No, since Python has such syntax, it must have its own application scenarios, such as for… Else syntax is the same. We can use mutable objects for caching.
For example, when calculating the factorial of a number, a dictionary of mutable objects can be used as the cache value to realize the cache. The cache stores the calculated value, and the second call does not need to repeat the calculation, directly from the cache.
def factorial(num, cache={}):
if num == 0:
return 1
if num not in cache:
print('xxx')
cache[num] = factorial(num - 1) * num
return cache[num]
print(factorial(4))
print("-- -- -- -- -- -- --")
print(factorial(4))Copy the code
Output:
-- First call -- XXX XXX XXX XXX XXX24-- Second call --24Copy the code
The second time it is called, the value is taken directly from the cache, so would you say that using mutable objects as default values is a Python bug? Not really, right?! You still use it as a feature.
Reference: docs.python.org/3/reference…
Blog: Foofish.net Public account: Zen of Python