The values of default arguments in python function [duplicate] - python

This question already has answers here:
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Closed 3 years ago.
I read the following tutorial:
https://docs.python.org/3/tutorial/controlflow.html#default-argument-values
and do some experiments.
I found that default argument is very confusing. Could anyone can explain the following phenomenon?
First, I tried following code:
Code 1
def f(a, L=[]):
L.append(a)
return L
print(f(1))
print(f(2))
print(f(3))
The results are:
[1]
[1, 2]
[1, 3, 3]
According to the document, this is because that default argument L is mutable and its value will be evaluated only once when the function is defined. That looks good!
However, I tried Code 2 next:
Code 2
def f(a, L=None):
if L is None:
L = []
L.append(a)
return L
print(f(1))
print(f(2))
print(f(3))
and the results are:
[1]
[2]
[3]
To study the default argument, I improved Code 2 and tried Code 3:
Code 3
def f(a, L=None):
print(type(L))
print(id(L))
print(L)
if L is None:
L = []
print(id(L))
L.append(a)
return L
print(f(1))
print(f(2))
print(f(3))
the results are:
<class 'NoneType'>
4381397320
None
4385607368
[1]
<class 'NoneType'>
4381397320
None
4385607496
[2]
<class 'NoneType'>
4381397320
None
4386251464
[3]
This shows that the L that before the if statement is always None and has different id with the id of L that in the if statement, and the former's id is fixed while the latter changes in every run.
I also tried the following code:
L_r = f(5)
print(id(L_r))
and the result is:
<class 'NoneType'>
4381397320
None
4385607496
4385607496
This shows that L_r has the same id as the L in the if statement.
Can you explain what the difference between L outside the if statement and in the if statement and why?
Thank you!

if you don't give L, default arguments will create a variable L, and reuse it for your mutiple calls, so in Code 1, it is edited in the same L.
but in Code 2, you change L in function:
if L is None:
L = []
default parameter L=None is always same(id=4381397320) as Code 1, but this statement make L refernce to a new variable, so the id will be different, and result will not be accumulated.
Hope that helps you, and comment if you have further questions. : )

Related

How do I implicitly define a parameter to an empty list? [duplicate]

This question already has answers here:
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Why does using `arg=None` fix Python's mutable default argument issue?
(5 answers)
Closed 3 years ago.
In the code below, I've been wondering how we could tell if a parameter, b, is given.
The problem is that the third call of func does not retain [5] in a newly created list but rather some pointer in the start that b is pointing to. I'm guessing this is defined before in the program stack entering func call itself, so the calling and returning of func would not change b...?
Any insight is appreciated.
def func(a, b=[]):
b.append([a])
print(b)
return b
func(3)
func(4, [])
func(5)
The best way is to assign the default value of b to something arbitrary (usually None) then check if b is defined that way:
def func(a, b=None):
if b is None:
b = []
b.append([a])
print(b)
return b
func(3)
func(4, [])
func(5)
You can define b to a default value, e.g. b=None, and then pick either the value of b if given, or pick an empty list.
def func(a, b=None):
lst = b or []
lst.append([a])
print(lst)
return lst
func(3)
#[[3]]
func(4, [])
#[[4]]
func(5)
#[[5]]

How is the behavior of local/global mutable object different from immutable object in python? [duplicate]

This question already has answers here:
How do I pass a variable by reference?
(39 answers)
Passing values in Python [duplicate]
(8 answers)
Python: Reassign value to variable (using a function) [duplicate]
(1 answer)
Why do list operations in python operate outside of the function scope? [duplicate]
(3 answers)
Closed 4 years ago.
Scenario 1 : Mutable object like list
def a(l):
l[0] = 1
print("\nValue of l = {0} in a()".format(l))
def b(l):
l = l + [9]
print("\nValue of l = {0} in b()".format(l))
l = [0]
a(l)
print("\nValue of l = {0} after executing a()".format(l))
b(l)
print("\nValue of l = {0} after executing b()".format(l))
Output
Value of l = [1] in a()
Value of l = [1] after executing a()
Value of l = [1, 9] in b()
Value of l = [1] after executing b()
Questions
In the context of mutable objects, why is the modification done to l in b() not visible in global scope whereas it happens for a()?
Scenario 2 : Immutable object like integer
def a(l):
l = 1
print("\nValue of l = {0} in a()".format(l))
def b(l):
l = l + 9
print("\nValue of l = {0} in b()".format(l))
l = 0
a(l)
print("\nValue of l = {0} after executing a()".format(l))
b(l)
print("\nValue of l = {0} after executing b()".format(l))
Output
Value of l = 1 in a()
Value of l = 0 after executing a()
Value of l = 9 in b()
Value of l = 0 after executing b()
Questions
In the context of immutable objects, why is the modification done to l in both a() and b() not visible in the global scope?
I checked with many folks and they couldnt explain this. Can someone explain the fundamental concepts used in this example?

Why can't a variable be set to equal a function call in Python? [duplicate]

This question already has answers here:
Why does [1].append(2) evaluate to None instead of [1,2]? [duplicate]
(2 answers)
Closed 6 years ago.
Please explain why c = None in the following Python 3 example, from the Python command line.
>>> a = [1,2]
>>> b = a
>>> c = b.append(3)
>>> print(a,b,c)
[1, 2, 3] [1, 2, 3] None
list.append() appends the entry in place and returns nothing which Python takes as None (default behavior of Python). For example:
>>> def foo():
... print "I am in Foo()"
... # returns nothing
...
>>> c = foo() # function call
I am in Foo()
>>> c == None
True # treated as true
You are assigning the return value of append which is None.
>>> a = [1,2]
>>> b = a.append(3)
>>> b == None
True
>>>
The function append doesn't return anything, that's why you have none in the variable. Let's see it better with a little example:
Let's say you have this function
def foo:
bar = "Return this string"
return bar
x = foo()
print(x) # x will be "Return this string"
Now let's say you have this function instead
def foo(bar):
print(bar)
x = foo(33) # Here 33 will be printed to the console, but x will be None
This happens because the return statement on the function, if you don't have any, the function will return None.
append is a function to do something in the list that you're calling it in (in Python strings are lists too), and this function doesn't need to return anything, it only modifies the list.

Understanding Python style, default parameters of functions [duplicate]

This question already has answers here:
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Closed 7 years ago.
This is covered by the Python Tutorial, but I still don't quite understand the reason why Python has this style. Is it purely convention, or is there some explanation behind why Python has the following style regarding default parameters:
My understanding is that Python prefers something=None as opposed to something=[] for default parameters for functions. But...why not use something=[]? Surely this is the convention in other languages, like C
As an example, take these two examples, which are equivalent
def function(arr, L=[]):
L.append(arr)
return L
and
def function(arr, L=None):
if L is None:
L = []
L.append(arr)
return L
My understanding is that the first is "incorrect style" for Python. Why?
EDIT: Ah, I finally understand. I am incorrect above: the two functions are NOT equivalent. Default arguments are evaluated once when the function is defined, not each time the function is called!
When you set a parameter to the value of a list, it is assigned when the function is defined, not when it is called. That is why you'll get different results for calling the function multiple times with the same input parameter. Beware!!!
def function(arr, L=[]):
L.append(arr)
return L
arr = [1, 2, 3]
>>> function(arr)
[[1, 2, 3]]
>>> function(arr)
[[1, 2, 3], [1, 2, 3]]
A default value that's an empty list will refer to one specific variable each time you call it, instead of creating a new empty list every time.
>>> def add(defList=[]):
defList.append(1)
return defList
>>> add()
[1]
>>> add()
[1,1]
It's a quirk of how mutable data works in Python. It could be occasionally useful, but often it's safer to use None. Then create an empty list if a value hasn't been passed.
The reason is that Python stores a value for L. In other words L is a reference to a constant. But that constant can be updated.
You can say that Python stores it as:
|--> []
|
function (arr, L)
But that [] is an ordinary object (thus with state), that can be modified as well. Now if the function can modify or return L, you start modifying the state of L. In the example:
def function(arr, L=[]):
L.append(arr)
return L
You modify L. If you call this the first time (for instance with function(123)), the object is updated so, now it is represented as:
|--> [123]
|
function (arr, L)
As a result the behavior of function depends on a global state. In general a global state is seen as a bad smell in code design and furthermore it is not what people might expect. This doesn't hold for None, since you modify the local reference L (not the object itself).
You can say that the object is the same, but each time you call the function, you copy a reference to a local variable L.
Now for the second case:
|--> None
|
def function(arr, L):
if L is None:
L = []
L.append(arr)
return L
If you call this method, you assign a value to L, but L itself is not global (only the object to which L refers). So if you call this method, after the if, the function (in progress) will look like.
Because the argument l is assigned an initial value of [] NOT when you call the function but when it's first declared and the module it's in is interpreted.
See: Python: Common Gotcha (Thanks #ferhat elmas)
Example: (showing what's going on)
$ python
Python 2.7.9 (default, Mar 19 2015, 22:32:11)
[GCC 4.8.4] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> def f(L=[]):
... L.append(1)
... return id(L), L
...
>>> def g(L=None):
... L = [] if L is None else L
... L.append(1)
... return id(L), L
...
We'll ignore what happens when you pass in another list to the L argument because that behaviour is well defined and acceptable. (takes a list, appends to it and returns it).
>>> f()
(139978918901088, [1])
>>> g()
(139978918964112, [1])
The first time we call f() and g() we might mistakenly think that we have successfully returned a new list from an initial empty one right? What what happens when we call f() again compared with the correct g():
>>> f()
(139978918901088, [1, 1])
>>> g()
(139978918964112, [1])
So the initial value you assigned for the argument L is set once and only only when the function is defined; not when it's called. This is a common gotcha as explained in the link aove.
NB: id() here returns the unique identity of objects so you can clearly see why the first is an incorrect way to define a default value for a mutable object such as a list.
Also Note: In the above examples that the identity of L does not change when calling f().

Why this local variable can keep its value in the function [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
“Least Astonishment” in Python: The Mutable Default Argument
I recently met a problem in Python.
Code:
def f(a, L=[]):
L.append(a)
return L
print f(1)
print f(2)
print f(3)
the output would be
[1]
[1, 2]
[1, 2, 3]
but why the value in the local list: L in the function f remains unchanged?
Because L is a local variable, I think the output should be:
[1]
[2]
[3]
I tried another way to implement this function:
Code:
def f(a, L=None):
if L is None:
L = []
L.append(a)
return L
This time, the output is:
[1]
[2]
[3]
I just don't understand why...
Does anyone have some ideas? many thanks.
The default parameters are in fact initialized when the function is defined, so
def f(L = []): pass
is quite similar to
global default_L = []
def f(L = default_L): pass
You can see this way that it is the same list object that is used in every invocation of the function.
The list in def f(a, L=[]) is defined as the function is defined. It is the same list every time you call the function without a keyword argument.
Setting the keyword to None and checking / creating as you have done is the usual work around for this sort of behaviour.

Categories

Resources