How is Python class attribute being set without actually being set? [duplicate] - python

This question already has answers here:
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Closed 8 years ago.
Can someone please explain what's going on in the following? Why on earth does object b have the value of object a's list?
class Test:
def __init__(self, A = []):
self.A = A
def __str__(self):
return str(self.A)
def mutate_A():
a = Test()
a.A.append(1)
return a
def problem_here():
a = mutate_A()
b = Test()
print b # why does this print [1] in python 2.7
problem_here()
Please let me know if I am being unclear or if more information is needed. Thank you.

Because in python, the default arguments are evaluated only once (when the function is defined). Therefore, all instances of the class use the same list A
If however, you want each instance to have its own list, then you should do:
def __init__(self):
self.A = []
>>> a = mutate_A()
>>> b = Test()
>>> print b
[]

Related

How to set the default of a class parameter to another class instance which has its own default parameters in Python [duplicate]

This question already has answers here:
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Closed last month.
I am trying to set the default variable of a class to some instance of another class which has its own default parameter. The problem I then face is that de setting of the parameter to the default does not seem to create a new instance.
Here is what I mean:
class TestA:
def __init__(self, x = 2):
self.x = x
class TestB:
def __init__(self, y = TestA()):
self.y = y
Now when I run
>>>> t = TestB()
I get what I expected: t.y.x = 2. To the best of my understanding, what happened was that __init__ set t.y = TestA(), and since TestA() was called without arguments, it set t.y.x = 2.
Finally I run t.y.x = 7.
In the next step I do this:
>>>> s = TestB()
>>>> s.y.x
7
I expected that s.y.x == 2.
Evermore so because when I just use TestA, then
>>>> a = TestA()
>>>> a.x = 7
>>>> b = TestA()
>>>> b.x
2
How come it doesn't work as expected for t and s?
Also, how can I properly use a construction like this where the default for attribute y is an instance of TestA with the default for the attribute x.
it's occours becouse the way dafault arguments work in Python.
You can use this approach:
class TestA:
def __init__(self, x=2):
self.x = x
class TestB:
def __init__(self, y=None):
if y is None:
y = TestA()
self.y = y

is there a way to bind parameter to a function variable in python? [duplicate]

This question already has answers here:
Python Argument Binders
(7 answers)
Closed 6 years ago.
Consider the following example:
class foo:
def __init__(self):
print ("Constructor")
def testMethod(self,val):
print ("Hello " + val)
def test(self):
ptr = self.testMethod("Joe") <---- Anyway instead of calling self.testMethod with parameter "Joe" I could simple bind the parameter Joe to a variable ?
ptr()
k = foo()
k.test()
In the defintion test is it possible for me to create a variable which when called calls the method self.testMethod with the parameter "Joe" ?
Either use a functools.partial() object or a lambda expression:
from functools import partial
ptr = partial(self.testMethod, 'Joe')
or
ptr = lambda: self.testMethod('Joe')
You could pass a name to the constructor (and store it on the class instance), this is then accessible to methods:
class Foo:
def __init__(self, name):
print("Constructor")
self.name = name
def testMethod(self):
print("Hello " + self.name)
def test(self):
self.testMethod()
As follows:
k = Foo("Joe")
k.test() # prints: Hello Joe

Instance attribute as default argument [duplicate]

This question already has answers here:
How to pass a default argument value of an instance member to a method?
(6 answers)
Closed 4 years ago.
I use Python 3 and I have small problem with kwargs. Is it possible to use instance attributes as a default argument value? I mean something like this:
class foo:
def __init__(self, a,b):
self.a = a
self.b = b
def setNewA(self, a=self.a):
print(a)
But I've got an error:
NameError: name 'self' is not defined
If I use class name I've got:
AttributeError: type object 'foo' has no attribute 'a'
I know that the other method is to use something like this:
def setNewA(self, a=None):
a = a or self.a
print(a)
But maybe there is some way to do it?
Not sure if this fits your use-case, but how about this?
>>> class foo:
... b = 1
...
... def setNewA(self, a=b):
... print(a)
>>>
>>> f = foo()
>>> f.setNewA()
1
>>> f.setNewA(2)
2

Using default values when initializing a superclass from a subclass in Python [duplicate]

This question already has answers here:
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Closed 9 years ago.
Why doesn't Python use the default argument values when initializing a superclass?
class Base:
def __init__(self, list=[]):
self.list = list
class Sub(Base):
def __init__(self, item):
Base.__init__(self)
self.list.append(item)
x = Sub(1)
y = Sub(2)
print x.list # prints [1, 2]
print y.list # prints [1, 2]
In this case, it seems that there is only one 'list' variable that's shared between the two instances of Sub. I can solve the issue by explicitly passing the value of 'list', i.e.:
class Base:
def __init__(self, list=[]):
self.list = list
class Sub(Base):
def __init__(self, item):
Base.__init__(self, list=[])
self.list.append(item)
x = Sub(1)
y = Sub(2)
print x.list # prints [1]
print y.list # prints [2]
Is there a way to avoid passing the argument explicitly in such cases? In my actual application, I have a lot of default values in my superclasses and it causes a lot of code duplication to pass them all again every time I initialize a subclass.
As Jon pointed out, you are mutating your default argument every time. If you want an empty list (but not the same one) to be the default, do this (noting how you set the new list to a full slice of the parameter):
class Base:
def __init__(self, list=None):
self.list = [] if list == None else list
class Sub(Base):
def __init__(self, item):
Base.__init__(self)
self.list.append(item)
x = Sub(1)
y = Sub(2)
print x.list # prints [1, 2]
print y.list # prints [1, 2]
Incidentally, you should probably avoid naming any variables list since that is the name of the list type.

Is this a bug in Python inheritance? [duplicate]

This question already has answers here:
Why do attribute references act like this with Python inheritance? [duplicate]
(3 answers)
How to avoid having class data shared among instances?
(7 answers)
Closed 10 years ago.
I'm not sure if the output of this code is correct or a bug:
class F:
"""An abstract class"""
list_of_secrets = []
def __init__(self):
pass
def getSecret(self):
return self.list_of_secrets
class F_None(F):
pass
class F_Some(F):
def __init__(self):
self.list_of_secrets.append("secret value!")
x = F_Some()
print "x:",x.getSecret()
y = F_None()
print "y:",y.getSecret()
The output using python 2.7.3:
x: ['secret value!']
y: ['secret value!']
I think it should output:
x: ['secret value!']
y: []
Any thoughts?
list_of_secrets is scoped to the class here. You want to instead attach it to self in __init__
def __init__(self):
self.list_of_secrets = []
You never define self.list_of_secrets. You only define F.list_of_secrets, which is entirely different. Do this instead:
class F:
"""An abstract class"""
def __init__(self):
self.list_of_secrets = []
def getSecret(self):
return self.list_of_secrets

Categories