1

I don't understand what is going on here. I have a class "C" that is a subclass of "B". "B" has an attribute "b" which is an instance of class "A". "A" has one attribute, a list called "a". When I initialize two different instances of "C", they have the same instance of "A" such that when I append to A.a it results in both instances of "C" having an appended attribute "b.a".

class A:
    a = list()

class B:
    b = A()

class C(B):
    pass

one = C()
two = C()
one.b.a.append('one')
one.b.a.append('two')
two.b.a.append('three')

print(one.b.a)
print(two.b.a)

Running this code prints out:

['one', 'two', 'three']
['one', 'two', 'three']

Clearly, one.b.a and two.b.a are pointing to the same object when I would have expected a new instance of "A" to be initialized whenever I call C(). Why is this happening and how do I fix it?

  • 1
    `A.a` is one list. Not one list per instance, but one list for the whole class. Also `B.b` is one instance of `A` for the whole class `B`. Class variables are different from instance variables, which matters when they refer to mutable objects like lists. – khelwood Aug 09 '20 at 21:06
  • 1
    `B` has a *class* attribute named `b`. You don't have separate lists for each *instance* of `B`. – chepner Aug 09 '20 at 21:07

1 Answers1

3

This is because you declared a and b as class attributes and NOT instance variables. You can think of class attributes as global variables for that class. There can only be one value for a class attribute for all objects.

On the the hand, instance variables are unique to each instance of the class. See this article for a more in depth explanation.

To summarize:

class A:
    a = 'A'
    
    def __init__(self):
        self.b = 'B'

foo = A()
bar = A()

foo.a
>>> A

bar.a
>>> A

foo.b
>>> B

bar.b
>>> B

A.a = 'C'
foo.a
>>> C
bar.a
>>> C

foo.b = 'D'
bar.b
>>> B
Jay Mody
  • 3,727
  • 1
  • 11
  • 27
  • 3
    A bit of this code is incorrect. Setting `foo.a = 'C'` would create an instance variable `foo.a`. It would not alter the value of `bar.a`. – khelwood Aug 09 '20 at 21:30
  • If you want to get the class variable from an instance, you need to call foo.__class__.a. Setting foo.a, as noted above, just creates an instance variable and doesn't affect bar. – Donnie Aug 09 '20 at 21:40
  • @khelwood woops, looks like you're right, made an edit – Jay Mody Aug 10 '20 at 00:57
  • 1
    adding my +1 because I didn't know the difference between class attributes and instance attributes and it was making me go mad!! – ciskoh Apr 22 '21 at 15:13