This question already has answers here:
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Closed 6 years ago.
I am noticing the following:
class c:
def __init__(self, data=[]):
self._data=data
a=c()
b=c()
a._data.append(1)
print b._data
[1]
Is this the correct behavior?
Yes, it's correct behavior.
However, from your question, it appears that it's not what you expected.
If you want it to match your expectations, be aware of the following:
Rule 1. Do not use mutable objects as default values.
def anyFunction( arg=[] ):
Will not create a fresh list object. The default list object for arg will be shared all over the place.
Similarly
def anyFunction( arg={} ):
will not create a fresh dict object. This default dict will be shared.
class MyClass( object ):
def __init__( self, arg= None ):
self.myList= [] if arg is None else arg
That's a common way to provide a default argument value that is a fresh, empty list object.
This is a classic pitfall. See http://zephyrfalcon.org/labs/python_pitfalls.html, section 5: "Mutable default arguments"
Always make functions like this then:
def __init__ ( self, data = None ):
if data is None:
data = []
self._data = data
Alternatively you could also use data = data or [], but that prevents the user from passing empty parameters ('', 0, False etc.).
Related
This question already has answers here:
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Closed 3 years ago.
I am implementing a basic node object in python. Basically, I implemented a node class with the attribute f_pointers and set it to the default value []. When ever I try to change f_pointers of (lets say) node_a, I will end up changing f_pointers of node_b, which are programmed to be completely unrelated.
I have already solved the problem by instead changing the default value to None and setting up the forward_pointers in __init__. However, I would still like to know how to avoid this problem in the future and possibly learn something new about Python.
For the sake of simplicity, I removed some unnecessary parts of the code.
class Node:
def __init__(self, f_pointers = []):
self.f_pointers = f_pointers
def get_pointers(self):
return self.f_pointers
def add_pointers(self, new_pointer):
self.f_pointers.append(new_pointer)
a = Node()
b = Node()
print(a.get_pointers, b.get_pointers)
>>> [] []
a.add_pointers("a")
print(a.get_pointers, b.get_pointers)
>> ["a"] ["a"]
a.add_pointers("b")
print(a.get_pointers, b.get_pointers)
>> ["a","b"] ["a","b"]
As can be seen, a and b are completely unrelated objects (other than the fact that they are of the same type Node) but will affect each other. Why does this happen?
It's because you are referencing to the same list (the one instantiated in the __init__ default params list definition like __init__(self, f_pointers=[]). What happens is that when you say in the __init__ method code block that self.f_points = f_pointers you are basically referencing the same list every time you instantiate a new Node object.
The reasons are explained further here
What you do want to do instead is instantiate a new list for every init like:
def __init__(self, f_pointers=None):
self.f_pointers = []
You should do it like this.
class Node:
def __init__(self, f_pointers=None):
if f_pointers:
self.f_pointers = f_pointers
else:
self.f_pointers = []
def get_pointers(self):
return self.f_pointers
def add_pointers(self, new_pointer):
self.f_pointers.append(new_pointer)
a = Node()
b = Node()
print(a.get_pointers(), b.get_pointers())
a.add_pointers("a")
print(a.get_pointers(), b.get_pointers())
You get this kind of behavior because in your case a.f_pointers and b.f_pointers is the same list, which was generated, when you described your class Node.
So a.f_pointers is b.f_pointers == True in your case
This question already has answers here:
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Closed 4 years ago.
I was tring to build a trie tree base on a word list, below is the node class defintion, self.next_trees for children of the node.
class Node(object):
def __init__(self, charactor, tree_dict={}):
self.charactor = charactor
self.next_trees = tree_dict
self.is_end = False
and build the tree use this function
def build_tree(words):
root = Node(None)
for word in words:
trace = root.next_trees
for i,c in enumerate(word):
if c not in trace:
trace[c] = Node(charactor=c)
pdb.set_trace()
if i == len(word)-1:
trace[c].is_end = True
else:
trace = trace[c].next_trees
else:
trace = trace[c].next_trees
return root
each time when the codes run to the break point, the object "trace" refers to is exactly the same object "trace[c].next_trees" refer to,rather than a empty dict"{}" enter image description here
and I copy similiar codes to a new .py file and run, it won't happen again.why it happens here?(python version 2.7.12)
You are using a mutable object as a default variable (tree_dict={}). Try doing this instead:
def __init__(self, charactor, tree_dict=None):
if tree_dict is None:
tree_dict = {}
The default value of tree_dict ({}) is evaluated only when the __init__ method is constructed, not when it is called. This default value is then stored and reused for every future call to __init__. This then means that all instances of Node initialized with no explicit value of tree_node will use this stored object as its tree_node, meaning that when you modify one, you modify all others as well.
This question already has answers here:
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Closed 8 years ago.
I made a list containing instances of a class, each of which has an empty list as an attribute. I was trying to append one of those lists on each iteration of my script, and instead all of them got appended. The code looks like this:
class generation:
def __init__ (self, number, container=[]):
"""This is the class containing lists"""
self.n=number
self.cont=container
class hybrid_variant:
def __init__ (self, generation):
"""Instances of this class go into lists in instances of generation"""
self.gen=generation
generation_list=[]
for x in range(3):
hybrid=hybrid_variant(generation= x+1)
new_generation=True
for elem in generation_list:
if elem.n == hybrid.gen:
new_generation=False
if new_generation==True:
generation_list.append(generation(hybrid.gen))
for elem in generation_list:
if elem.n == hybrid.gen:
elem.cont.append(hybrid)
Instead of getting one element in each container attribute of all generations every generation has all of the three elements.
As described in this question mutable default parameters are stored by reference, so if all instances of your generation type will have a reference to to the same list object. As such, changing one will change it for every other instance.
To fix this, just don’t use an empty list as the default argument, but construct the empty list in the method instead:
class generation:
def __init__ (self, number, container=None):
self.n = number
self.cont = [] if container is None else container
This question already has answers here:
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Closed 9 years ago.
I have a little problem that I do not understand.
I have a method:
def appendMethod(self, newInstance = someObject()):
self.someList.append(newInstace)
I call this method without attributes:
object.appendMethod()
And actually I append the list with the same instance of someObject.
But if I change it to:
def appendMethod(self):
newInstace = someObject()
self.someList.append(newInstance)
I get new instance of that object every time, what's the difference?
Here's an example:
class someClass():
myVal = 0
class otherClass1():
someList = []
def appendList(self):
new = someClass()
self.someList.append(new)
class otherClass2():
someList = []
def appendList(self, new = someClass()):
self.someList.append(new)
newObject = otherClass1()
newObject.appendList()
newObject.appendList()
print newObject.someList[0] is newObject.someList[1]
>>>False
anotherObject = otherClass2()
anotherObject.appendList()
anotherObject.appendList()
print anotherObject.someList[0] is anotherObject.someList[1]
>>>True
This is because you are assigning your default argument as a mutable object.
In python, a function is an object that gets evaluated when it is defined, So when you type def appendList(self, new = someClass()) you are defining new as a member object of the function, and it does NOT get re-evaluated at execution time.
see “Least Astonishment” in Python: The Mutable Default Argument
This question already has answers here:
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Closed 6 years ago.
I'm trying to append items to a list in an instantiated object:
class Room(object):
def __init__(self, name, contents=[]):
self.name = name
self.contents = contents
living_room = Room('living room')
dining_room = Room('dining room')
print(living_room.contents) # []
print(dining_room.contents) # []
living_room.contents.append('lamp')
print(living_room.contents) # ['lamp']
print(dining_room.contents) # ['lamp']
The behavior I would expect would be for the lamp to be in living_room.contents, but not in dining_room.contents. Can anyone explain this?
contents is a mutable default argument. It's default value (the empty list) is only created once, not for every call.
Here are more details:
http://docs.python-guide.org/en/latest/writing/gotchas/