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.
Related
This question already has answers here:
How do I pass a variable by reference?
(39 answers)
Closed 2 years ago.
I write the following program to see how function works in Python
def mylove(a):
a=a+1
a=2
mylove(a)
print(a)
When I print(a), why it is 2 not 3
On the other hand for the following code
def puzzle(name):
name.pop()
name=['s','s','s']
puzzle(name)
print(name)
We have the result ['s','s']
Integers are immutable data types, so they cannot be changed.
The line
a +=1
is a shortcut for
a=a+1
What is happening is that inside the scope of the function, the variable label 'a' is being assigned to a new different integer, while the original integer as an object in memory remains unchanged.
Outside of the function, the label 'a' still points to the original unchanged integer object.
The id() function can be used to show you if the underlying memory object of two variables is the same
>>> a =1
>>> def f(a):
... print('before',id(a))
... a+=1
... print('after',id(a))
...
>>> print('outside',id(a));f(a);print('outside',id(a))
outside 504120010720
before 504120010720
after 504120010752
outside 504120010720
>>>
The after object is a different object than the other three.
In contrast, something mutable like a list can be used to send changes out of a function.
>>> b=[]
>>> def g(b):
... print('before',id(b))
... b.append(5)
... print('after',id(b))
...
>>> print('outside',id(b));g(b);print('outside',id(b))
outside 504083879680
before 504083879680
after 504083879680
outside 504083879680
>>> print(b)
[5]
The after object is the same object as the other three.
The bottom line is that append() modifies the same object in memory, modifications are only allowed for mutable objects.
Doin a += does not modify the original object but produces an entirely new object which is assigned to the variable name.
As explained in this other answer, sys.getrefcount() returns a value 1 higher than expected because when invoking the function for an object, a new reference to that object has to be created. For example, the following would print 2, instead of of 1:
import sys
class Foo:
pass
foo = Foo()
print(sys.getrefcount(foo)) # returns 2
I am making a python c extension and want to add some regression tests to make sure the reference counting is incrementing and decrementing as expected.
Is it safe to have a test that always returns sys.getrefcount(...) - 1 ?
class TestFoo(unittest.TestCase):
def assert_reference_count(self, obj, expected_reference_count):
self.assertEquals(sys.getrefcount(obj) - 1, expected_reference_count)
I figure there must be a reason why sys.getrefcount doesn't do this automatically. Perhaps there are some instances where it would not increment by 1, for example.
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".
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.
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.