This question already has answers here:
How to avoid having class data shared among instances?
(7 answers)
Closed 8 years ago.
I am a little bit confused by this example:
>>> class A:
... foo = []
>>> a, b = A(), A()
>>> a
<__main__.A instance at 0x0000000002296A88>
>>> b
<__main__.A instance at 0x0000000002296F88>
>>> a.foo.append(5)
>>> a.foo
[5]
>>> b.foo
[5]
1) How does python connect two different instances?
2) Does the instance refer to a class A() or foo attribute after appending the value?
But when i add __init__ method, things look different:
>>> class A:
... def __init__(self):
... self.foo = []
...
>>> a, b = A(), A()
>>> a
<__main__.A instance at 0x00000000021EC508>
>>> b
<__main__.A instance at 0x0000000002296F88>
>>> a.foo.append(5)
>>> a.foo
[5]
>>> b.foo
[]
3) What is the magic of __init__ ?
In the first case, the foo = [] is done at class definition time, and thus a single list is associated with the class, rather than the instance.
In the second case, the self.foo = [] is done at instance initialization time (which is what __init__ is - instance initialization), and thus a separate list is associated with each instance.
In your first example, foo is a class attribute, not an instance attribute. This means it's shared across all the instances of A, which you can check with:
a1 = A()
a2 = A()
print a1.foo is a2.foo
print a1.foo is A.foo
In your second example, however, self.foo = [] makes foo an instance attribute, built independently for each instance of A.
Related
This question already has answers here:
Python class variables or class variables in general
(4 answers)
Differences between static and instance variables in python. Do they even exist?
(6 answers)
Compound assignment to Python class and instance variables
(5 answers)
Class (static) variables and methods
(27 answers)
Closed 5 years ago.
>>> class a:
... b=5
... def __init__(self,x,y):
... self.x=x
... self.y=y
...
>>> p=a(5,6)
>>> q=a(5,6)
>>> a.b
5
>>> a.b+=1
>>> p.b
6
>>> q.b
6
>>> q.b-=1
>>> q.b
5
>>> p.b
6
>>> a.b
6
As you see, on changing the class variable by an instance's method, the same doesn't gets reflected in the class variable and the class variable of other instance. Why is it so?
Because q.b -= 1 creates an instance variable with the name b, look in your __dict__:
q.__dict__
{'b': 4, 'x': 5, 'y': 6}
p.__dict__
{'x': 5, 'y': 6}
q.b is different than a.b, you've shadowed a.b after the assignment. Take note that this isn't a Python 3 specific issue, Python 2 also behaves in the same way.
This is clearly stated in the assignment statement section of the Language Reference:
Note: If the object is a class instance and the attribute reference occurs on both sides of the assignment operator, the RHS expression, a.x can access either an instance attribute or (if no instance attribute exists) a class attribute. The LHS target a.x is always set as an instance attribute, creating it if necessary. Thus, the two occurrences of a.x do not necessarily refer to the same attribute: if the RHS expression refers to a class attribute, the LHS creates a new instance attribute as the target of the assignment:
class Cls:
x = 3 # class variable
inst = Cls()
inst.x = inst.x + 1 # writes inst.x as 4 leaving Cls.x as 3
This description does not necessarily apply to descriptor attributes, such as properties created with property().
We say classes are mutable in Python which means you can using references we can change the values that will be reflected in object. For example,
>>> A = [1, 2, 3]
>>> B = A
>>> B[2] = 5
>>> A
[1, 2, 5]
Here I can change the values of A object using B because list is a mutable type. My question is why can't I change the attributes of a class below using same concept:
class C:
apple = 2
def __init__(self):
self.dangerous = 2
D = C # D is pointing to same class C
D().dangerous = 5 # changing the value of class attribute D
D().apple = 3 # changing the value of apple here
print D().apple
print D().dangerous
OUTPUT:
2
2
Could anyone explain why the output is 2 and 2 but not 3 and 5 since we are saying that the class is a mutable type.
UPDATE : Referring to the answer by #zxq9, if you see the below diagram when do D=C, D is actually pointing to the same class rather a new object as you have described. Could you explain this:
Each time you place parens after a class, you are constructing a new instance object of the class. So the things you printed were brand-spanking new and did not reflect the short-lived assignments you had made previously.
Here is an example (expanded to cover the underlying reference to class C):
>>> class C:
... red = 2
... def __init__(self):
... self.blue = 2
...
>>> C.red
2
>>> C.blue
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: type object 'C' has no attribute 'blue'
>>> C().red
2
>>> C().blue
2
>>> #OOOOH!
...
>>> z = C()
>>> z.red
2
>>> z.blue
2
>>> D = C
>>> D.red
2
>>> D().red
2
>>> D().red = "over 9000!"
>>> D.red
2
>>> D.red = "No, really over 9000!"
>>> D.red
'No, really over 9000!'
>>> C.red
'No, really over 9000!'
>>> #OOOOOOHHHH!
...
Note that we did change the class directly when I assigned D.red = "No, really over 9000!" -- because that was referencing the class definition itself, not an instantiated object created from it. Note also that assigning an attribute of D (a copy) changed the attribute of C (the original) because in many (but not all) cases Python makes such assignments by reference, meaning that D is really an alias of C, not copy of the underlying structure. Read up on Python's deepcopy() method for more about that particularly startling detail.
Walk through the example code carefully, note the difference between referencing ClassName and calling ClassName(). The first is a reference via a variable name to a class definition -- a blueprint for generating instance objects that carries a constructor function __init__() with it. The second is an invokation of __init__() whose return value is an instance object of the class within which it is defined.
This is also why you can do things like this:
def some_fun(another_fun, value):
another_fun(value)
def foo(v):
return v + v
def bar(v):
return v * v
some_fun(foo, 5)
some_fun(bar, 5)
This feature lends Python a high degree of flexibility in building functional abstractions. (Now if only it had tail-call elimination...)
It is an interesting example.
The line D().dangerous = 5 will change the attribute "dangerous" of the instance D(); But the line print D().dangerous print out the attribute "dangerous" of ANOTHER instance D().
The line D().apple = 3 will create an attribute "apple" in the instance D() since this instance does not have the attribute "apple".
The line print D().apple will print out the attribute "apple" of the class D since the instance D() does not have the attribute "apple".
One way to change the attribute "apple" of the class through its instance is by using D().__class__.apple=3
I have the class
>>> class Foo:
... ls=[]
...
>>> f1=Foo()
>>> f2=Foo()
>>> f1.ls.append(1)
>>> f1.ls.append(2)
>>> print f1.ls
[1, 2]
>>> print f2.ls
[1, 2] #I expect its result is empty [], why
>>> f2.ls=[]
>>> print f1.ls
[1, 2]
>>> print f2.ls
[]
# If f1.ls and f2.ls refer to the same list, since i modify f2.ls,
# the f1.ls is empty ,too. Does the statement 'f2.ls=[]' add new attribute
# to f2. Where do f1.ls and f2.ls refer and how it happens
I want to use one class and declare many variables. If I hope all variables have different lists. Do I do like this
class Foo:
pass
f1=Foo()
f2=oo()
f1.ls=[]
f2.ls=[]
do others
Are there some more simple and better methods.
Forgive my ignorant for python class. Thanks in advance
Class-level assignments create class variables. To create instance variables, do so in the constructor:
def __init__(self):
self.ls = []
Defining a variable directly inside a class gives a class-level variable. Thus, ls isn't unique to all instances, but is instead a property of the class Foo. It can still be accessed, however, through its instances, which is what you did.
class Foo:
ls = []
So that:
>>> f1 = Foo()
>>> f2 = Foo()
>>> Foo.ls.append(1)
>>> Foo.ls
[1]
>>> f1.ls
[1]
>>> f2.ls
[1]
An instance level variable is unique to each instance, and can be defined in the __init__ function, as such:
class Foo:
def __init__(self):
self.ls = []
In this way, class Foo has no attribute ls; rather, each instance constructed with __init__ does:
>>> f1 = Foo()
>>> f2 = Foo()
>>> Foo.ls.append(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: class Foo has no attribute 'ls'
>>> f1.ls.append(1)
>>> f1.ls
[1]
>>> f2.ls
[]
When you say
class Foo:
ls=[]
ls is defined as a class variable and all the objects which you create will will have a variable with the same name and that variable will point to the current value in the class's ls value.
When you say
f1.ls.append(1)
You are actually mutating the original object. That's why the change is reflected in f2 as well (since they both are referring to the same object). But when you say
f2.ls = []
You are actually creating a variable on the f2 object, which refers to an empty list object. Now, the ls object is different from f1's ls. You can confirm this with this statement
print f1.ls is f2.ls # Will print False
print f1.ls is Foo.ls # Will print True
If you actually wanted to get a new object whenever you created an object. You have to create an instance variable, like this
class Foo:
def __init__(self):
self.ls = []
f1, f2 = Foo(), Foo()
print f1.ls is f2.ls # Will print False
Now you are binding ls to the current instance of the class and making it point to an empty list. So, this will be different for each instance.
ls is a static variable as you defined. you self.ls in the init so you can have different ls in the memory.
This is a completely theoretical question. Suppose the following code:
>>> class C:
... a = 10
... def f(self): self.a = 999
...
>>>
>>> C.a
10
>>> c = C()
>>> c.a
10
>>> c.f()
>>> c.a
999
At this point, is class variable C.a still accessible through the object c?
Yes, though c.__class__.a or type(c).a. The two differ slightly in that old-style classes (hopefully, those are all dead by now - but you never know...) have a type() of <type 'instance'> (and __class__ works as expected) while for new-style classes, type() is identical to __class__ except when the object overrides attribute access.
All class variables are accessible through objects instantiated from that class.
>>> class C:
... a = 10
... def f(self): self.a = 999
...
>>> C.a
10
>>> c = C()
>>> c.a
10
>>> c.f()
>>> c.a
999
>>> c.__class__.a
10
>>> c.a
999
>>> del(c.a)
>>> c.a
10
Attributes are first searched within the object namespace and then class.
Yes, you can access a from an object c, à la c.a. The value would initially be 10.
However, if you call c.f(), the value of c.a will now be 999, but C.a will still be 10. Likewise, if you now change C.a to, say, 1000, c.a will still be 999.
Basically, when you instantiate an instance of C, it will use the class variable as its own a value, until you change the value of that instance's a, in which case it will no longer "share" a with the class.
After you assign to it on the class instance, there is both a class attribute named a and an instance attribute named a. I illustrate:
>>> class Foo(object):
... a = 10
...
>>> c = Foo()
>>> c.a
10
>>> c.a = 100 # this doesn't have to be done in a method
>>> c.a # a is now an instance attribute
100
>>> Foo.a # that is shadowing the class attribute
10
>>> del c.a # get rid of the instance attribute
>>> c.a # and you can see the class attribute again
10
>>>
The difference is that one exists as an entry in Foo.__dict__ and the other exists as an entry in c.__dict__. When you access instance.attribute, instance.__dict__['attribute'] is returned if it exists and if not then type(instance).__dict__['attribute'] is checked. Then the superclasses of the class are checked but that gets slightly more complicated.
But at any rate, the main point is that it doesn't have to be one or the other. A class and an instance can both have distinct attributes with identical names because they are stored in two separate dicts.
Can someone explain why Python does the following?
>>> class Foo(object):
... bar = []
...
>>> a = Foo()
>>> b = Foo()
>>> a.bar.append(1)
>>> b.bar
[1]
>>> a.bar = 1
>>> a.bar
1
>>> b.bar
[1]
>>> a.bar = []
>>> a.bar
[]
>>> b.bar
[1]
>>> del a.bar
>>> a.bar
[1]
It's rather confusing!
This is because the way you have written it, bar is a class variable rather than an instance variable.
To define an instance variable, bind it in the constructor:
class Foo(object):
def __init__(self):
self.bar = []
Note that it now belongs to a single instance of Foo (self) rather than the Foo class, and you will see the results you expect when you assign to it.
As others have said the code as written creates a class variable rather than an instance variable. You need to assign in __init__ to create an instance variable.
Hopefully this annotated copy of your code is helpful in explaining what's going on at each stage:
>>> class Foo(object):
... bar = [] # defines a class variable on Foo (shared by all instances)
...
>>> a = Foo()
>>> b = Foo()
>>> a.bar.append(1) # appends the value 1 to the previously empty list Foo.bar
>>> b.bar # returns the value of the class variable Foo.bar
[1]
>>> a.bar = 1 # binds 1 to the instance variable a.bar, masking the access
>>> a.bar # you previously had to the class variable through a.bar
1
>>> b.bar # b doesn't have an instance variable 'bar' so this still
[1] # returns the class variable
>>> a.bar = [] # bind a's instance variable to to an empty list
>>> a.bar
[]
>>> b.bar # b doesn't have an instance variable 'bar' so this still
[1] # returns the class variable
>>> del a.bar # unbinds a's instance variable unmasking the class variable
>>> a.bar # so a.bar now returns the list with 1 in it.
[1]
Also, printing out the value of Foo.bar (the class variable accessed via the class rather than via an instance) after each of your statements might help clarify what is going on.
When you declare an element in the class like that it is shared by all instances of the class. To make a proper class member that belongs to each instance, separately, create it in __init__ like the following:
class Foo(object):
def __init__(self):
self.bar = []
In the beginning, bar is a class variable and it is shared between a and b, both a.bar and b.bar refer to the same object.
When you assign a new value to a.bar, this does not overwrite the class variable, it adds a new instance variable to the a object, hiding the class variable when you access a.bar. If you delete a.bar (the instance variable), then a.bar resolves again to the class variable.
b.bar on the other hand always refers to the class variable, it's not influenced by the additional bar on the a object or any values assigned to that.
To set the class variable you can access it through the class itself:
Foo.bar = 1
>>> class Foo(object):
... bar = []
...
bar is a shared class variable, not an instance variable. I believe that deals with most of your confusion. To make it a instance var, define it in class's __init__ per the other answers.
>>> a = Foo()
>>> b = Foo()
>>> a.bar.append(1)
>>> b.bar
[1]
This is the proof of that.
>>> a.bar = 1
>>> a.bar
1
>>> b.bar
[1]
Now you've redefined a.bar as a instance variable. That's what happens when you define variables externally by default.
>>> a.bar = []
>>> a.bar
[]
>>> b.bar
[1]
>>> del a.bar
>>> a.bar
[1]
Same again. b.bar is still the shared class variable.
On a related note, you should be aware of this pitfall that you might see sometime soon:
class A:
def __init__(self, mylist = []):
self.mylist = mylist
a = A()
a2 = A()
a.mylist.append(3)
print b.mylist #prints [3] ???
This confuses a lot of folks and has to do with how the code is interpreted. Python actually interprets the function headings first, so it evaluates __init__(self, mylist = []) and stores a reference to that list as the default parameter. That means that all instances of A will (unless provided their own list) reference the original list. The correct code for doing such a thing would be
class A:
def __init__(self, mylist=None):
if mylist:
self.mylist = mylist
else:
self.mylist = []
or if you want a shorter expression you can use the ternary syntax:
self.mylist = mylist if mylist else []