0

I have a problem in understanding of the notion of static variables in Python classes. According to Static class variables in Python, whenever we define a variable outside a method and inside a python class, this variable is static. This means this variable can be accessed without any need to instantiate an object from a class and can be accessed by the class name directly. For example:

class my_class:

     i=12

     def __init__(self,j):
        self.j=j


instance=my_class(10)

my_class.i:
>12
instance.i:
>12
instance.i=13
instance.i:
>13
my_class.i:
>12 

You can see that we can access the static variable i through both instance object and the class name. However, when we change the value of i for instance object it does not affect the value of the class(my_class.i is still 12). On the other hand, things totally change if we are working with array static variables. Considering the similar example:

class my_class:

     i=[]

     def __init__(self,j):
        self.j=j


instance=my_class(10)

my_class.i:
>[]
instance.i:
>[]
instance.i.append(13)
instance.i:
>[13]
my_class.i:
>[13]

You can see that when I change the variable for the array of instance object it also affects the class value. What is going on here? I would appreciate if someone could help me better understand this issue as it is not that much obvious to me. By the way, I have a Java background.

Infintyyy
  • 929
  • 1
  • 11
  • 23

3 Answers3

4

Assignment to an instance attribute normally sets an instance attribute. Once there is an instance attribute, it masks the class attribute.

So before you executed instance.i=13, there was no i attribute on the instance, only on the class. You then bound the name i on the instance to 13, and the next time you look up instance.i that attribute is found, and no attempt is made to find my_class.i anymore.

But manipulating a mutable object is not the same thing as assigning to a class or instance attribute. You did not assign to instance.i, you altered the list object referenced by my_class.i and visible through instance.i. instance.i still will find the my_class.i attribute, you never used = to create the instance.i attribute.

That's because you only ever read the instance.i reference to the list, in order to print it, and to find the list.append() method. At no point do you set a new value for the instance.i attribute reference.

The list object is it's own object, one that you can alter by adding or removing or replacing the values referenced by the indices of the list. It doesn't matter what names or attributes reference that list, you can have any number of such references to the same list, and altering the list won't change those references.

Try creating more references to the list, and see what happens:

>>> list_named_i = instance.i
>>> list_named_i
[13]
>>> my_class.i.append(42)
>>> list_named_i
[13, 42]
>>> list_named_i[0] = 81
>>> instance.i
[81, 42]

instance.i, my_class.i and list_named_i are all just different references to the same list object, and you can modify (mutate) that list object via any of those references.

I recommend you read up on how Python names and attributes and lists and such work; see Facts and myths about Python names and values by Ned Batchelder.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
1
instance.i.append(13)

This doesn't change the variable, it mutates the list object. The variable is still set to the same object.

If you set the variable to a new list:

instance.i = [13]

then you'll see what you're expecting.

Sean Van Gorder
  • 3,393
  • 26
  • 26
-2

During instantiation, the object receives a copy (more or less) of each of the class attributes, at the time of the instantiation. This is why you can access and mutate the value on the instance without impacting the class, and visa/versa.

Your second point wherein the list is modified, seemingly in reverse from the instance to the class is due to the mutability of the list and is similar to the issue often encountered with default arguments, described here.

joebeeson
  • 4,159
  • 1
  • 22
  • 29
  • This is just wrong. Class attributes are not copied at all; rather, when `a.x` fails to find an attribute `x` on the object `a`, Python next attempts to find a class attribute `x` in each class in the object's method-resolution order. You can absolutely modify a mutable class attribute via an instance of that class. – chepner Mar 09 '18 at 18:17
  • @chepner a mutable, sure. But to the novice developer (as the OP seems to be), it looks an awful lot like copying as assignment to an instance attribute isn't reflected "up" to the class attribute. But sure if you want to be pedantic about it, and overly specific to a novice question. Then yes, it's not copied in a literal sense. – joebeeson Mar 09 '18 at 19:04