Python creating new object of class contains values of older object [duplicate] - python

This question already has answers here:
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Closed 7 years ago.
I have got a class with a list-variable and a function to append items to this list. I cannot append items directly, because I have to validate them:
class Foo:
def __init__(self, elements=[]):
self.elements = elements
def append(self, element):
self.elements.append(element)
If I instantiate an object of this class, add items and then create another new object, this object contains the items of the first object.
foo = Foo()
print foo.elements # []
foo.append(element=4)
print foo.elements # [4]
foo.append(element=7)
print foo.elements # [4, 7]
bar = Foo()
print bar.elements # [4, 7]
Can someone explain my, why this happens?
A possible solution for me could be this, but I don't like it...
class Foo:
def __init__(self, elements=None):
if elements is None:
self.elements = []
else:
self.elements = elements
def append(self, element):
self.elements.append(element)
Thanks for all answers!

You've got your answer yourself already. Initializing with a mutable object ist never a good idea. It will be initialized once the class gets defined and reused for every instance.

Related

List inheritance: "extend" vs "+" vs "+=" [duplicate]

This question already has answers here:
Why does += behave unexpectedly on lists?
(9 answers)
Different behaviour for list.__iadd__ and list.__add__
(3 answers)
Closed 3 years ago.
I would like to understand why the following three, extremely simple, python codes give (apparently) inconsistent answers. It's just a toy exercise to understand what's happening, not a real-world problem. The class myList(list) inherits from list and defines a method "add" that should mimic the well-known "extend" method. I've tried to do this in three ways, with the direct use of extend, with the + operator and with +=. However, with great surprise, I get inconsistent answers. Notice that, among the three codes, a single line (inside the add method) changes.
CODE 1: self.extend(item)
class myList(list):
def add(self,item):
self.extend(item)
l = myList([1,2])
l.add([3,4])
print(l)
Prints [1, 2, 3, 4]
CODE 2: self = self + item
class myList(list):
def add(self,item):
self = self + item
l = myList([1,2])
l.add([3,4])
print(l)
Prints [1, 2]
CODE 3: self += item
class myList(list):
def add(self,item):
self += item
l = myList([1,2])
l.add([3,4])
print(l)
Prints [1, 2, 3, 4]
I am a bit confused... what's happening?
Thank you in advance.
self = self + item
does not modify the object, only the local variable inside the method.
By using + the interpreter is calling __add__ in list, which creates a new list, and you're then making the self variable point to it within the add method, and when the method exits its value is discarded.
On the other hand, the += version calls the __iadd__ method of the list class, which updates its contents as expected.

Parameter passing of self defined class object in python: call-by-reference or call-by-value evaluation? [duplicate]

This question already has answers here:
How do I pass a variable by reference?
(39 answers)
Closed 4 years ago.
When you call 'append' with a list, the pointer of the list is passed in, so the original data is changed.
>>> tmp = [1,2]
>>> tmp.append(3)
>>> tmp
[1, 2, 3]
Here comes my question. For the following code, when I pass the Node object named 'head' into the method 'get', I expect that head would point to the second node with value 2 after calling the method. However, it turns out that 'head' is still pointing to the first node with value 1.
>>> class Node:
def __init__(self, val):
self.val = val
self.next = None
def get(self, index):
for i in range(index):
self = self.next
return self.val
>>> head = Node(1)
>>> head.next = Node(2)
>>> head.get(1)
2
>>> head.val
1
Can anyone explain why is this happening? To my understanding, as Node is mutable, a reference should be passed in. How can I know whether a reference or value is passed in?
Thanks in advance!
Python passes the value of a reference to an object when making a call instead of actually passing the object reference itself. In this case, self is just a local variable with a copy of a reference to the Node object that the get method is bound to, so if you assign to self with something else (self.next in your case), all that happens is for the local variable self to hold a reference to a different object, but the object that self originally referred to, as well as the reference to the object from the caller, remains untouched.
Just to add on to #blhsing, you're doing local reassignment (nothing to do with call-by-value or reference), and you can verify this with:
a1 = [1]
def expand(a):
a = a + [2]
return a
a2 = expand(a1)
print(a1) # prints [1]
print(a2) # prints [1, 2]

mutable default value in python [duplicate]

This question already has answers here:
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Closed 9 years ago.
class Foo:
def __init__(self, stuff = []):
print(stuff)
self.stuff = stuff
def add(self,x):
self.stuff.append(x)
>>>f = Foo()
[]
>>>f.add(1)
>>>g = Foo()
[1]
And I make only one change to the code(line 4)
class Foo:
def __init__(self, stuff = []):
print(stuff)
self.stuff = stuff or []
def add(self,x):
self.stuff.append(x)
>>>f = Foo()
[]
>>>f.add(1)
>>>g = Foo()
[]
I change something in the line 4 but resulting in the change in the printing outcome(which is in the line 3)
I just want to know how it works.
The list you pass as fallback parameter value is instantiated once and reused throughout every call to __init__. So adding a value into it will propagate to every next instance of Foo.
The difference between the first and second examples is stuff or []. You need to know that an empty list, when evaluated as a boolean value is False, so the stuff or [] expression returns the second operand if stuff is empty, which will always be the case in your example. The second operand is a list that gets instantiated in the method call, so is a different instance everytime __init__ is called. This ensures that the added value doesn't propagate.
In this line:
def __init__(self, stuff = []):
all instances created with Foo() will use the same list, created as the default value for all calls of __init__. That's why in the first example adding anything to f results in modifying the default list for all further instance of Foo. This is because the part stuff = [] is evaluated when the interpreter evaluates the def line. From the documentation:
Default parameter values are evaluated when the function definition is executed. This means that the expression is evaluated once, when the function is defined, and that the same “pre-computed” value is used for each call.
(bold added by me).
In the second example, as the default is always empty the second part of the or clause is evaluated which creates a new, individual instance of a list each time a new instance of Foo is created. So all Foo instances will have their own lists.
Here you can find more explanations and examples.

In python __init__ dictionary arguments defaulting to previous class values [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
“Least Astonishment” in Python: The Mutable Default Argument
I'm finding that dictionary arguments to the init() function of my class are defaulting to values I've previously set in previous instances. I really don't understand this behavior, and it doesn't seem to happen with lists or basic variables. Example code:
class TestClass:
def __init__(
self,
adir={},
alist=[],
avar=None
):
print("input adir: " + str(adir)) #for test2, shows test1.mydir
self.mydir = adir
self.mylist = alist
self.myvar = avar
test1 = TestClass()
test1.mydir['a'] = 'A'
test1.mylist = ['foo']
test1.myvar = 5
test2 = TestClass()
print(test2.mydir) #has same value of test1!
print(test2.mylist)
print(test2.myvar)
The output looks like this:
initializing test1
input adir: {}
initializing test2
input adir: {'a': 'A'}
{'a': 'A'}
[]
None
Why does the dictionary argument (adir) to test2 get set to test1.mydir? Especially, why is the behaviour different than other mutable types like list?
Thank you!
As DSM says, don't modify mutable default arguments. Do this instead:
class TestClass:
def __init__(self, adir=None, alist=None, avar=None):
if alist is None:
alist = []
if adir is None:
adir = {}

Python class constructor with default arguments [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
“Least Astonishment” in Python: The Mutable Default Argument
Can anyone explain the following strange behaviour?
I have the following class:
class Zoo:
def __init__(self,alist=[]):
self.animals = alist
def __len__(self):
return len(self.animals)
def add(self,a):
self.animals.append(a)
and when I do the following,
In [38]: z=Zoo()
In [39]: z.add(2)
In [40]: z.add(23)
In [41]: len(z)
Out[41]: 2
In [42]: z2=Zoo()
In [43]: len(z2)
Out[43]: 2
Why is z2.animals not an empty list?
Thanks, Matthias
You are mutating the default argument in your constructor (you are just copying a reference to the same list into each of your instances). You can fix this as follows:
class Zoo:
def __init__(self,alist=None):
self.animals = alist or []
def __len__(self):
return len(self.animals)
def add(self,a):
self.animals.append(a)
The default argument list is the same object for all instances, hence assigning it to a member just assigns a reference to the same object.
here's an example:
>>> class foo():
... def __init__(self, x = []):
... print id(x)
...
>>> x = foo()
140284337344168
>>> y = foo()
140284337344168
>>> z = foo()
140284337344168
you can see that x is the same object in all instances.

Categories

Resources