This question already has answers here:
How to avoid having class data shared among instances?
(7 answers)
Closed 6 years ago.
I don't understand the difference in the following example. One time an instance of a class can CHANGE the class variable of another instance and the other time it can't?
Example 1:
class MyClass(object):
mylist = []
def add(self):
self.mylist.append(1)
x = MyClass()
y = MyClass()
x.add()
print "x's mylist: ",x.mylist
print "y's mylist: ",y.mylist
Output:
x's mylist: [1]
y's mylist: [1]
So here an instance x of class A was able to access and modify the class attribute mylist which is also an attribute of the instance y of A.
Example 2:
class MyBank(object):
crisis = False
def bankrupt(self) :
self.crisis = True
bankX = MyBank()
bankY = MyBank()
bankX.bankrupt()
print "bankX's crisis: ",bankX.crisis
print "bankY's crisis: ",bankY.crisis
bankX's crisis: True
bankY's crisis: False
Why does this not work in this example?
In first case there is no assignment in add method:
def add(self):
self.mylist.append(1) # NOT self.mylist = something
In second case there is an assignment:
def bankrupt(self) :
self.crisis = True # self.crisis = something
When an attribute is set on instance, it is always set on particular instance only (it's put to instance's __dict__ atribute). Class __dict__ is unaffected.
In first case there is no assignment, so standard look-up rules apply. Since there is no "mylist" in __dict__ attribute of instance, it falls back to class __dict__.
Operation performed in add mutates value stored in MyClass.__dict__. That's why change is observable in all instances.
Consider following snippet (it may explain your issue better):
class MyClass:
x = []
x1 = MyClass()
x2 = MyClass()
x3 = MyClass()
x1.x.append(1)
print x1.x # [1]
print x2.x # [1]
print x3.x # [1]
assert x1.x is x2.x is x3.x
x3.x = "new" # now x3.x no longer refers to class attribute
print x1.x # [1]
print x2.x # [1]
print x3.x # "new"
assert x1.x is x3.x # no longer True!
Related
Consider the following example:
class Ciao():
a = 1
def whatIsIt(self):
print(self.a)
i = Ciao()
i.a = 2
i.whatIsIt() #Returns 2
I am creating an instance of the class Ciao, modifying the class variable and then printing it in the last line. To me, the class variable a, after having create the object i, behaves completely as the instance variable in this code snippet.
class Ciao2():
def __init__(self):
self.a = None
def whatIsIt(self):
print(self.a)
i = Ciao2()
i.a = 2
i.whatIsIt() #Returns 2
Is there any fundamental difference between a class and an instance variable after having instantiate the object?
Yes, there is a fundamental difference. It can be seen when the variables are mutable (e.g. lists), and you work with several instances of the same class. Changes made to a class variable will carry over to all the instances, but changes made to an instance variable will not:
class Ciao():
a = [1]
def __init__(self):
self.b = [1]
i1 = Ciao()
print(i1.a, i1.b) # [1] [1]
i1.a.append(2)
i1.b.append(2)
print(i1.a, i1.b) # [1, 2] [1, 2]
i2 = Ciao()
print(i2.a, i2.b) # [1, 2] [1]
I just started to learn Python and I"m struggling a little with instance variables. So I create an instance variable in a method that's of a list type. Later on, I want to call and display that variable's contents. However, I'm having issues doing that. I read some online, but I still can't get it to work. I was thinking of something along the following (this is a simplified version):
What would the proper way of doing this be?
class A:
def _init_(self):
self.listVar = [B("1","2","3"), B("1","2","3")]
def setListVal():
#Is this needed? Likewise a "get" method"?
def randomMethod():
A.listVar[0] #something like that to call/display it right? Or would a for
#for loop style command be needed?
Class B:
def _init_(self):
self.a = ""
self.b = ""
self.c = ""
Is the list something you'll be passing to the instance when you create it (i.e. will it be different each time)?
If so, try this:
class A:
def __init__(self, list):
self.listVar = list
Now, when you instantiate (read: create an instance) of a class, you can pass a list to it and it will be saved as the listVar attribute for that instance.
Example:
>>> first_list = [B("1","2","3"), B("1","2","3")]
>>> second_list = [C("1","2","3"), C("1","2","3")]
>>> first_instance = A(first_list) # Create your first instance and pass it your first_list. Assign it to variable first_instance
>>> first_instance.listVar # Ask for the listVar attribute of your first_instance
[B("1","2","3"), B("1","2","3")] # Receive the list you passed
>>> second_instance = A(second_list) # Create your second instance and pass it your second_list. Assign it to variable second_instance
>>> second_instance.listVar # Ask for the listVar attribute of your second_instance
[C("1","2","3"), C("1","2","3")] # Receive the list you passed second instance
Feel free to ask if anything is not clear.
class A:
def __init__(self):
self.listVar = [B("1","2","3"), B("1","2","3")]
def setListVal(self, val):
self.listVar[0] = val # example of changing the first entry
def randomMethod(self):
print self.listVar[0].a # prints 'a' from the first entry in the list
class B:
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
I made several changes. You need to use self as the first argument to all the methods. That argument is the way that you reference all the instance variables. The initialization function is __init__ note that is 2 underscores before and after. You are passing three arguments to initialize B, so you need to have 3 arguments in addition to self.
It seems that in Python, to declare a variable in a class, it is static (keeps its value in the next instances). What better way to get around this problem?
class Foo():
number = 0
def set(self):
self.number = 1
>>> foo = Foo()
>>> foo.number
0
>>> foo.set()
>>> foo.number
1
>>> new_foo = Foo()
>>> new_foo.number
1
Variables defined at the class level are indeed "static", but I don't think they work quite the way you think they do. There are 2 levels here which you need to worry about. There are attributes at the class level, and there are attributes at the instance level. Whenever you do self.attribute = ... inside a method, you're setting an attribute at the instance level. Whenever python looks up an attribute, it first looks at the instance level, if it doesn't find the attribute, it looks at the class level.
This can be a little confusing (especially if the attribute is a reference to a mutable object). consider:
class Foo(object):
attr = [] #class level attribute is Mutable
def __init__(self):
# in the next line, self.attr references the class level attribute since
# there is no instance level attribute (yet)
self.attr.append('Hello')
self.attr = []
# Now, we've created an instance level attribute, so further appends will
# append to the instance level attribute, not the class level attribute.
self.attr.append('World')
a = Foo()
print (a.attr) #['World']
print (Foo.attr) #['Hello']
b = Foo()
print (b.attr) #['World']
print (Foo.attr) #['Hello', 'Hello']
As others have mentioned, if you want an attribute to be specific to an instance, just initialize it as an instance attribute in __init__ (using self.attr = ...). __init__ is a special method which is run whenever a class is initialized (with a few exceptions that we won't discuss here).
e.g.
class Foo(object):
def __init__(self):
self.attr = 0
Just leave the declaration out. If you want to provide default values for the variables, initialize them in the __init__ method instead.
class Foo(object):
def __init__(self):
self.number = 0
def set(self):
self.number = 1
>>> foo = Foo()
>>> foo.number
0
>>> foo.set()
>>> foo.number
1
>>> new_foo = Foo()
>>> new_foo.number
0
Edit: replaced last line of the above snippet; it used to read 1 although it was just a typo on my side. Seems like it has caused quite a bit of confusion while I was away.
You maybe want to change the class attribute:
class Foo():
number = 0
def set(self):
Foo.number = 1
instead of overriding it!
i have a question about class attribute in python.
class base :
def __init__ (self):
pass
derived_val = 1
t1 = base()
t2 = base()
t2.derived_val +=1
t2.__class__.derived_val +=2
print t2.derived_val # its value is 2
print t2.__class__.derived_val # its value is 3
The results are different. I also use id() function to find t2.derived_val and t2.__class__.derived_val have different memory address.
My problem is derived_val is class attribute. Why it is different in above example?
Is it because the instance of class copy its own derived_val beside the class attribute?
There are class attributes, and instance attributes.
When you say
class base :
derived_val = 1
You are defining a class attribute. derived_val becomes a key in
base.__dict__.
t2=base()
print(base.__dict__)
# {'derived_val': 1, '__module__': '__main__', '__doc__': None}
print(t2.__dict__)
# {}
When you say t2.derived_val Python tries to find 'derived_val' in t2.__dict__. Since it is not there, it looks if there is a 'derived_val' key in any of t2's base classes.
print(t2.derived_val)
print(t2.__dict__)
# 1
# {}
But when you assign a value to t2.derived_val, you are now adding an instance attribute to t2. A derived_val key is added to t2.__dict__.
t2.derived_val = t2.derived_val+1
print(t2.derived_val)
print(t2.__dict__)
# 2
# {'derived_val': 2}
Note that at this point, there are two derived_val attributes, but only
the instance attribute is easily accessible. The class attribute becomes accessible only through referencing base.derived_val or direct access to the class dict base.__dict__.
Check it out here and here.
The __class__ attribute is the class that the object belongs to. So in your example, the situation is similar to static variables. The t2.__class__.derived_val is referring to the variable that belongs to the class, not the variable that belongs to t2.
Another way (perhaps a more concise one) to demonstrate this:
class A():
a1 = []
x = A()
y = A()
x.a1.append("test")
x.a1, y.a1
(['test'], ['test'])
class B():
b1 = None
def __init__(self):
self.b1 = list()
r = B()
s = B()
r.b1.append("test")
r.b1, s.b1
(["test"], [])
How do these 2 classes differ?
class A():
x=3
class B():
def __init__(self):
self.x=3
Is there any significant difference?
A.x is a class variable.
B's self.x is an instance variable.
i.e. A's x is shared between instances.
It would be easier to demonstrate the difference with something that can be modified like a list:
#!/usr/bin/env python
class A:
x = []
def add(self):
self.x.append(1)
class B:
def __init__(self):
self.x = []
def add(self):
self.x.append(1)
x = A()
y = A()
x.add()
y.add()
print("A's x:", x.x)
x = B()
y = B()
x.add()
y.add()
print("B's x:", x.x)
Output
A's x: [1, 1]
B's x: [1]
Just as a side note: self is actually just a randomly chosen word, that everyone uses, but you could also use this, foo, or myself or anything else you want, it's just the first parameter of every non static method for a class. This means that the word self is not a language construct but just a name:
>>> class A:
... def __init__(s):
... s.bla = 2
...
>>>
>>> a = A()
>>> a.bla
2
A.x is a class variable, and will be shared across all instances of A, unless specifically overridden within an instance.
B.x is an instance variable, and each instance of B has its own version of it.
I hope the following Python example can clarify:
>>> class Foo():
... i = 3
... def bar(self):
... print 'Foo.i is', Foo.i
... print 'self.i is', self.i
...
>>> f = Foo() # Create an instance of the Foo class
>>> f.bar()
Foo.i is 3
self.i is 3
>>> Foo.i = 5 # Change the global value of Foo.i over all instances
>>> f.bar()
Foo.i is 5
self.i is 5
>>> f.i = 3 # Override this instance's definition of i
>>> f.bar()
Foo.i is 5
self.i is 3
I used to explain it with this example
# By TMOTTM
class Machine:
# Class Variable counts how many machines have been created.
# The value is the same for all objects of this class.
counter = 0
def __init__(self):
# Notice: no 'self'.
Machine.counter += 1
# Instance variable.
# Different for every object of the class.
self.id = Machine.counter
if __name__ == '__main__':
machine1 = Machine()
machine2 = Machine()
machine3 = Machine()
#The value is different for all objects.
print 'machine1.id', machine1.id
print 'machine2.id', machine2.id
print 'machine3.id', machine3.id
#The value is the same for all objects.
print 'machine1.counter', machine1.counter
print 'machine2.counter', machine2.counter
print 'machine3.counter', machine3.counter
The output then will by
machine1.id 1
machine2.id 2
machine3.id 3
machine1.counter 3
machine2.counter 3
machine3.counter 3
I've just started learning Python and this confused me as well for some time. Trying to figure out how it all works in general I came up with this very simple piece of code:
# Create a class with a variable inside and an instance of that class
class One:
color = 'green'
obj2 = One()
# Here we create a global variable(outside a class suite).
color = 'blue'
# Create a second class and a local variable inside this class.
class Two:
color = "red"
# Define 3 methods. The only difference between them is the "color" part.
def out(self):
print(self.color + '!')
def out2(self):
print(color + '!')
def out3(self):
print(obj2.color + '!')
# Create an object of the class One
obj = Two()
When we call out() we get:
>>> obj.out()
red!
When we call out2():
>>> obj.out2()
blue!
When we call out3():
>>> obj.out3()
green!
So, in the first method self specifies that Python should use the variable(attribute), that "belongs" to the class object we created, not a global one(outside the class). So it uses color = "red". In the method Python implicitly substitutes self for the name of an object we created(obj). self.color means "I am getting color="red" from the obj"
In the second method there is no self to specify the object where the color should be taken from, so it gets the global one color = 'blue'.
In the third method instead of self we used obj2 - a name of another object to get color from. It gets color = 'green'.