default arguments in Python [duplicate] - python

This question already has answers here:
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Closed 9 years ago.
I am confusing about the default arguments in Python. Here are my code:
#!/usr/bin/env python
import sys
class Node(object):
def __init__(self, ID=str(), items=[]):
self.ID = ID
self.items = items
if __name__ == '__main__':
a = Node('1')
b = Node('2')
b.items.append('sth.')
c = Node('3')
print a.items
print b.items
print c.items
The output is:
['sth.']
['sth.']
['sth.']
I just change the b instance. Why all instances are changed?

This is a case of reference. Since the list items is mutable (can be directly changed with an internal method), any change you make to it in any function is reflected in all references to it.
For example, if you have the following code:
def f(x):
x.append(5)
a = [1, 2, 3]
f(a)
# a is now [1, 2, 3, 4], even in the global scope
This occurs because a is a mutable list, and so it is passed by reference.
So when you use items = [], you are creating a blank list when the program is started, but not every time you create a new instance. Instead, each instance refers to the same list, created when the class was "declared". So, since each instance refers to the same list, they are all changed.
To fix this, change your constructor to:
def __init__(self, ID=str(), items=None): # you can also use '' instead of str()
if not items: items = []
# everything else
A few good links to explain this better/in a different way:
Immutable Objects and References
Mutable Default Arguments
Default List Arguments
There are a ton of other questions like this out there, just search [python] None as default argument.

Related

Bizarre Python dictionary behavior using objects as keys [duplicate]

This question already has answers here:
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Closed 7 years ago.
I'm defining a dictionary within a class (let's call it Thingy). Sometimes I need to use other Thingy instances as keys in the dictionary for thing1. However, if I try to add thing2 as a key with value 1 (say) to the dictionary of thing1, then thing2:1 also appears in the dict of thing2!
Please confirm if you can reproduce (python 2.7.6):
# class definition
class Thingy:
def __init__(self, d={}):
self.dict = d
# Test code
thing1 = Thingy()
thing2 = Thingy()
thing1.dict[thing2] = 1
print('Thing1.dict is {}'.format(thing1.dict))
print('Thing2.dict is {}'.format(thing2.dict))
gives me
Thing1.dict is {<__main__.Thingy instance at 0x7f79fdeed998>: 1}
Thing2.dict is {<__main__.Thingy instance at 0x7f79fdeed998>: 1}
even though I never changed Thing2.dict!
Never use mutable objects as default parameter:
class Thingy:
def __init__(self, d={}):

python constructor default argument list [duplicate]

This question already has answers here:
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Closed 9 years ago.
Is the default argument list the same object for all instances?
class X():
def __init__(self,a=[]):
self.member=a
print id(a)
k=X([1,2,3])
g=X([1,2,3])
t=X()
h=X()
The output surprises me:
140072782781832
140072782901976
140072782816536
140072782816536
As you can see, the id is different when a equals [1,2,3] but stays the same when a is empty. However, if I delete self.member, now the code looks like this:
class X():
def __init__(self,a=[]):
print id(a)
k=X([1,2,3])
g=X([1,2,3])
t=X()
h=X()
The output becomes like this:
140033294171528
140033294171528
140033294206232
140033294206232
The id stay the same when a equals [1,2,3].
I am totally confused... Anyone can explain that?
Yes, which is why you are supposed to do
class X():
def __init__(self, a=None):
self.a = [] if a is None else a
Edit:
I would point out that
class X():
def __init__(self,a=[]):
print(id(a))
k = X([1,2,3])
g = X([1,2,4]) # <- different list values
t = X()
h = X()
also gives
42678232
42678232
42680152
42680152
so I would expect the answer is something like "if you create a list, delete it, and create another list, the odds are good it will reuse the same allocated memory location".

Unexpected python behavior related to default argument for mutable object [duplicate]

This question already has answers here:
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Closed 8 years ago.
#! /usr/bin/python
class my_class:
# 1. __init__
def __init__(self):
self.my_set = set()
# 2. __init__
#def __init__(self, arg_set = set()):
# self.my_set = arg_set
c1 = my_class()
c1.my_set.add('a')
print c1.my_set
c2 = my_class()
c2.my_set.add('b')
print c1.my_set
my_class has 2 ways of defining __init__:
If I use 1st way, output is as expected:
set(['a'])
set(['a'])
If I use 2nd way, output is unexpected:
set(['a'])
set(['a', 'b'])
What is wrong with 2nd way? How can modification of C2 (a separate object), result in modification of c1?
Edit: Updated the question title to reflect specific area of concern
From http://docs.python.org/2/reference/compound_stmts.html#function-definitions
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. This is especially important to understand when a
default parameter is a mutable object, such as a list or a dictionary:
if the function modifies the object (e.g. by appending an item to a
list), the default value is in effect modified.
Thats the reason why your second method appends values everytime.
Moreover, modify the second __init__ like this
def __init__(self, arg_set = set()):
print id(arg_set)
self.my_set = arg_set
Now, when you run the code, you ll always get the same address (id function in CPython returns the address of the object in memory). So, default arguments are not created everytime the function is invoked, but when evaluated the first time.
thefourtheye is completely right on the cause of this behavior. If you want to use the second version of your constructor, it should be like this:
def __init__(self, arg_set=None):
if arg_set is None:
self.my_set = set()
else:
self.my_set = arg_set

Unexpected behaviour with Python namespace (2.7 and 3) [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
“Least Astonishment” in Python: The Mutable Default Argument
I just want to check that the way I'm expecting this to work is correct or not. Here is a simplified version of a class I'm writing:
class Foo(object):
def __init__(self):
pass
def bar(self, test1=[], test2=[]):
if test2:
test1.append(1)
print test1
Now, to me, test1 and test2 - unless set - should always be set as empty lists when the function bar is called. This means that when test1 is printed, there should only ever be one item in the list (provided you only give one item as an argument). However, this is not the case:
>>> i = Foo()
>>> i.bar()
[]
>>> i.bar(test2=[1])
[1]
>>> i.bar()
[1, 1]
>>> i.bar(test2=[1])
[1, 1, 1]
In this case, you'd expect a similar result using integers:
class Foo(object):
def __init__(self):
pass
def bar(self, test1=0, test2=0):
if test2:
test1 += 1
print test1
But here, test1 is always set to 0:
>>> i = Foo()
>>> i.bar()
0
>>> i.bar(test2=1)
1
>>> i.bar(test2=1)
1
>>> i.bar(test2=1)
1
It seems that the list is persistent in the function or class's namespace, but the integer is not.
This may be a misunderstanding on my part, so would just like some clarification.
The default arguments of a function are set when the function is declared, not every time you call the function. Therefore the list that is made when the function is declared is only made once, and is referenced every other time you called the function. See also this question.
Because lists are mutable, when you change them, anything that references it changes as well. However integers are immutable (they can't be changed), so when you reassign a variable to another integer, only that variable changes, because it is referencing a different object.

Understanding Python inheritance and initialisation [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
“Least Astonishment” in Python: The Mutable Default Argument
In Python 2.7, consider I have the following code:
class Base(object):
# Variant 1
def __init__(self, records=[]):
self._records = records
# Variant 2
# def __init__(self, records=[]):
# self._records = []
# if records:
# self._records = records
def append(self, value):
self._records.append(value)
class ChildA(Base):
pass
class ChildB(Base):
pass
a = ChildA()
b = ChildB()
a.append(100)
b.append(200)
print a._records
print b._records
If I use variant 1 to initialize my base class, self._records behaves like a class variable. Executing the code using variant 1 to initialize my base class, I get the ouput:
[100, 200]
[100, 200]
Using variant 2 to initialize my base class, self._records behaves like a instance variable (as expected). Executing the code using variant 2 to initialize my base class, I get the output:
[100]
[200]
What is the difference between these both variants? Why does variant 1 work different to variant 2? Thanks a lot for your help!
Your default argument is [], which is a common pitfall with Python. See more in the tutorial:
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.
It has nothing to do with inheritance, class or instance variables. Consider the next code:
>>> def f(a=[]):
... a.append(1)
... print a
...
>>> f.func_defaults
([],)
>>> f()
[1]
>>> f()
[1, 1]
>>> f.func_defaults
([1, 1],)
Default values for function parameters are evaluated only ones and stored within function object. Each time f is called it operates with the same list. Right as in your case.
As the others had put - this has to do with using an empty list as default value, not with class inheritance behavior at all.
The correct way to do it is:
class Base(object):
# Variant 1
def __init__(self, records=None):
if records is None:
records = []
self._records = records
Therefore ensuring a new list instance is created each time the Base class is instantiated. The way you put it in your code, a list object is instantiated when your class body is parsed by Python - and that one instance of list is used as the default parameter each time the __init__ method is run. As the objects hold a reference and change that same list, the change is visible in all other objects which share the Base class.

Categories