Python - why it doesn't create a new instance of object? [duplicate] - python

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

Related

Member variable getting shared across multiple objects- python [duplicate]

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.

python object member refer to itself incorrectly [duplicate]

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.

Python: Appending an instance attribute for instances in a list [duplicate]

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

Default value in a function in Python [duplicate]

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.).

Appending to a list in an instantiated object [duplicate]

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/

Categories

Resources