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.
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 created a class to store some variables and dictionary. Each object will have its own dictionary. However when I created a Class in certain way, it resulted in dictionary getting shared across all objects created.
When I tweaked the init, I was able to achieve what I wanted. I want to know why dictionary got shared across different objects and when and I would that be useful.
Snippet 1: (Where dictionary gets populated and shared across all object instances)
class A(object):
def __init__(self, x, y={}):
self.x = x
self.y = y
def set_y(self,key,value):
self.y[key] = value
Snippet 2:(Where dictionary value is unique and not shared between member instances)
class A(object):
def __init__(self,x):
self.x = x
self.y = {}
def set_y(self,key,value):
self.y[key] = value
Test Driver:
l = "abcdefghijklmnopqrsqtuwxyz"
letter_list = []
node = None
for count, letter in enumerate(l,1):
if node:
letter_list.append(node)
node = A(letter)
node.set_y(letter,count)
I would like to know why dictionary got updated for all instances in first case and not for the second case
The dictionary is updated because of the way you used the default value in the __init__ constructor. In the first case, that empty dict is a single object; it is not a unique constructor for each new object. It gets evaluated when the class is defined, and the same dict object sits there for each new object instantiated. Very simply, the line
def __init__(self, x, y={}):
is executed once, when the function is defined, during the class definition.
In your second case, the initialization self.y = {} is in the body of the function, and so gets executed each time you instantiate a new object. You can see the longer explanation in this canonical posting on the topic.
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 this simple class:
class revs:
def __init__(self, rev, us, accs = []):
self.rev = rev
self.us = us
self.accs = accs
And i have this piece of code to asign values to the list and is inside of a loop
rev, usu = cada_l.split("|")
acct = each_l[:2].strip()
list_acct.append(acct)
and last, i create a dict, to manage a list of revs like this:
drevs = {}
cada = revs(rev, us, list_acct)
drevs[cada.rev] = cada
And it Works correctly with rev and us, but with list_acct is ever updating all the instances:
drevs['1'].rev
'1'
drevs['2'].rev
'2'
drevs['1'].us
'user1'
drevs['2'].us
'user2'
drevs['1'].accs
'["Doc1","Doc2"]'
drevs['2'].accs
'["Doc1","Doc2"]'
And if i change list_acct.clear(), the values in all the instances is clear, I'm still fairly new to Python and this confuses me.
Thanks
This looks like it's happening because you're passing the same list to every object. As a result, all the objects maintain references to the same list, and since list is mutable, it appears to change "all" of them at once.
To fix this, either pass in a new empty list each time you create a revs object, or else clone the list you're passing in:
cada = revs(rev, us, list_acct[:])
Note that if list_acct contains mutable objects, you could still get into the same problem again, but one level deeper!
If you're not passing lists to the revs objects when you create them at all (I can't tell, since you're not showing your full code!), then you have the same problem, but for a different reason: in Python, default arguments are all evaluated once, at the time of the function definition. Therefore, you can get this behavior:
r1 = revs(1, 1)
r2 = revs(2, 2)
r1.accs.append("Hi!")
print(r1.accs) # prints ['Hi!']
print(r2.accs) # prints ['Hi!']
Because the default argument for the revs constructor is always pointing to the same list. See this question for an explanation as to why, but to get around it, just use None as your default instead of [].
class revs:
def __init__(self, rev, us, accs=None):
self.rev = rev
self.us = us
if accs is None:
accs = []
self.accs = accs
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.).