This question already has answers here:
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Closed 8 years ago.
def word_to_syllable(w,li=[]):
if not w:
return li
pattern = """
########
"""
pattern = re.sub("C","[^aeiou]",pattern)
pattern = re.sub("V","[aeiou]",pattern)
match = re.findall(pattern,w,re.VERBOSE)[0]
#print(li)
li.append(match)
w = w[len(match):]
return word_to_syllable(w,li)
This works okay for the first call, but then local variable li somehow doesn't get forgotten and new values are just appended to the old ones - instead of string, as name of the function suggests, being split to it's own list. Yeah, if I define my function without default argument and instead say it's empty list later in the call , everything's just fine, but I'm curious about what is exactly happening with this code above.
Using a list as a default argument in Python will produce surprising results because it's mutable, so (w,li=[]) isn't what you want.
See here: "Least Astonishment" and the Mutable Default Argument
and here: http://www.deadlybloodyserious.com/2008/05/default-argument-blunders/
Short version is that default arguments are evaluated when the function is defined, not when the function is run like you'd expect.
Related
This question already has answers here:
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Why does PyCharm warn about mutable default arguments? How can I work around them?
(5 answers)
Closed 9 months ago.
I have the following code:
def my_function(x, l=[]):
for i in range(x):
l.append(i+i)
print(l)
my_function(2)
my_function(3)
For the first function call it prints [0,2] which is ok, but then for the second call it prints [0,2,0,2,4]!!! Why? I expected it to be only [0,2,4] since my second argument (or the absence of it) is an empty list. Am I doing something wrong? Am I assuming something wrong? Any help will be greatly appreciated.
It's printing that because the list object does not change between function calls. Try creating a new list object for each call like in the following modified code:
def my_function(x, l):
for i in range(x):
l.append(i+i)
print(l)
my_function(2, list())
my_function(3, list())
This question already has answers here:
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Closed 2 years ago.
Here, the list is defined as a local variable in the parameter of the function foo, but I'm confused why even on repeated calls the list still remembers it's previous values, why is it acting like a static variable?
def foo(character,my_list = []):
my_list.append(character)
print(my_list)
foo("a")
foo('b')
foo('c')
---- Output ----
['a']
['a','b']
['a','b','c']
When you define a mutable value as default argument, what python does is something like this:
default_list = []
def foo(character, my_list=default_list):
my_list.append(character)
The same happens for any other mutable type (dict for instance).
You can find a very detailed explanation here: https://docs.quantifiedcode.com/python-anti-patterns/correctness/mutable_default_value_as_argument.html
One way you can make an empty list as a default for a list can be:
def foo(character, my_list=None):
if my_list is None:
my_list = []
my_list.append(character)
This is one of the common gotchas:
Python’s default arguments are evaluated once when the function is
defined, not each time the function is called (like it is in say,
Ruby). This means that if you use a mutable default argument and
mutate it, you will and have mutated that object for all future calls
to the function as well.
This question already has answers here:
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Closed 6 years ago.
I am confused by the following example:
def append(elem, to=None):
if to is None:
to = []
to.append(elem)
return to
append(5)
# Out: [5]
append(3) # A new list is created
# Out: [3]
The point here is supposed to be that you are resetting "to" to an empty list at the beginning of every function call to avoid the following:
append(5)
# Out: [5]
append(3) # A new list is created
# Out: [5, 3]
But how can you check that "to" is None to set it to []? It seems to me that you are either pulling in the "to" defined in the definition, or you are pulling in the "to" modified by the last call. How does this work?
When you define a function with a default argument, the function uses that default values for that argument if it is not supplied. So in the case of append(5), to is not specified, so the function assumes the value of to to be None - it's effectively the same as calling append(5, None).
So now, the function checks if to is None, which it is, so to gets reassigned to an empty list. 5 gets appended to the list, and the list is returned.
When you make the second call append(3), it is again as if you called append(3, None). Again, the if to is None evaluates to True, and to is reassigned to an empty list. Then, 3 is appended to that empty list, and the list is returned.
Since the default argument (in this case None) is immutable, the operations on to do not persist the end of the function call. The function has its own chunk of memory which gets cleared when the function returns.
However, if the default argument was mutable (like for instance []), that value is created when the function is defined (i.e. when python sees def func(arg1, arg2=[]), it creates an empty list in memory and uses that list every time this function is called). Thus, any changes made to that list will persist the end of the function, since that mutable default argument was created when the function was defined (before the function was ever called).
I'll be quoting Common Gotchas — The Hitchhiker's Guide to Python here:
For example like:
def append_to(element, to=[]):
to.append(element)
return to
A new list is created once when the function is defined, and the same
list is used in each successive call.
Python’s default arguments are evaluated once when the function is
defined, not each time the function is called (like it is in say,
Ruby). This means that if you use a mutable default argument and
mutate it, you will and have mutated that object for all future calls
to the function as well.
Also if you don't like to use none ant consider it an antipattern, you can use more fancier kind of this:
def append_to(element, to=list):
if callable(to):
to = to()
to.append(element)
return to
I don't know if it's better though
This question already has answers here:
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Closed 9 years ago.
In the following function L stores the values during every call.
For example, if I call f(1), L is now [1]. When I call it again the previous L is appended with the new value. So now L is [1,1].
def f(a, L=[]):
L.append(a)
return L
But in this function:
i = 5
def f(arg=i):
print arg
i = 6
No matter how many times I call this function, the argument is still 5 - it does not remain updated between calls.
What is the reason why this does not get updated but the list does?
This is a good question. The reason why this happens is because the default arguments to functions are stored as single objects in memory, not recreated every time you call the function.
So when you have a list [] as a default argument, there will only be one list forever for the duration of that program. So when you add to the list you are adding to that one copy of the list. This is still true for numbers like 5. However, numbers are immutable in Python, and so when you alter a default argument that starts at a number, you're really making it point to a new number and not editing that 5 object, whereas many operations on lists mutate the list in place rather than returning a new list.
http://docs.python.org/3/tutorial/controlflow.html#default-argument-values
Important warning: The default value is evaluated only once. This
makes a difference when the default is a mutable object such as a
list, dictionary, or instances of most classes.
The recommended solution, if you need the behaviour of an empty list default argument without having the same empty list default argument every call to the function, is to do this:
def f(a, L = None):
if L is None:
L = []
L.append(a)
return L
The creation of an empty list is now evaluated on individual calls to the function - not once.
a.append() is different from i = 6. The first changes the array but the array stays the same object (the same but not equal). The latter on the other hand assigns a totally new value to a variable. It does not change any object (ints aren't mutable anyway).
This question already has answers here:
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Closed 8 years ago.
Sorry if the question isn't that clear in the title but I had a limited amount of space,
To clarify...
In my code I have a class called empDB that creates an empty list for storing objects.
Too add new objects to the list I created a method in empDB called appendEmp. appendEmp takes one parameter that specifies the class which it will instantiate and then add it to the list. If no parameter is provided it defaults to the class Employee().
here is my problem
when I call the method appendEmp it creates new objects in the list, however when I try to instantiate an object thats already in the list it will only create an alias of it. I need it to create a new one each time so that i can edit the attributes of each object in the list individually.
here is a snippet of my code:
class empDB:
def __init__(self, lst= []): #creates empty list
self.lst = lst
def appendEmp(self, emp=Employee()):
self.lst=self.lst+[emp]
basicly this is what happens now
>>> db=empDB()
>>> db.appendEmp()
>>> db.appendEmp()
>>> db[0] is db[1]
True
I need it to be false
You can solve your problem like that:
class empDB:
def __init__(self, lst= []): #creates empty list
self.lst = lst
def appendEmp(self, emp=None):
if emp is None:
empt = Employee()
self.lst=self.lst+[emp]
The issue was caused by mutable value being assigned as the default argument. I replaced it with None, although if you would like to accept this value as the argument of appendEmp(), you can replace it with something else.
More on dangers of using mutable default arguments you can read here: Hidden features of Python: Dangers of mutable default arguments
Ps. You may also wish to do the same for __init__() method.
A mutable default argument can cause trouble if you aren't fully aware of how they work. Most beginners don't realize that the default value for a keyword argument is computed exactly once, when the definition is processed. If the function code modifies this object thereafter then the next call to use the default object will see the modified value.
I explain this with pictures starting at http://holdenweb.com/files/IntroNotes.pdf#page=118
These notes, by the way, are the draft of an open source release of my "Introduction to Python" class, so I'd appreciate any feedback.