Search code examples
pythonclassoopvariablesinstance

Misunderstanding of classes in python, accessing and changing instance variables


I am trying to change the instance variable, var3, associated with an instantiated object of Class3. I want to make this change by passing the instance variable, var3, to methods of another class (see code below).

I instantiate Class2 and Class3 in Class1's init, yielding the objects self.class2, self.class3.

Then I send the instance variable self.class3.var3 to a method (change_class3_var) defined in Class2, which appends a string to var3.

But when I go to print the instance variable, var3 (class1.class3.var3), the variable does not change as expected, it has not been appended to and remains just "class3" (instead of "class3 appended by class2".

I am looking for different answers along the lines of:

  1. Why this doesn't work
  2. Why this code style is a bad idea and what you think is the best alternative for achieving this behavior of fluidly passing/altering instance variables among various classes

This code is not used for anything critical, I am just trying to further my understanding. The idea is to use a base class (Class1) to glue all the classes together in a PyQt application, passing and changing instance variables fluidly using a collection of instantiated classes objects.

Thank you!!

class Class1:
    var = "class1"

    def __init__(self):
        self.class2 = Class2()
        self.class3 = Class3()

    def class2_to_class3(self):
        self.class2.change_class3_var(self.class3.var3)


class Class2:
    var2 = "class2"

    def change_class3_var(self, instance_var):
        instance_var += " appended by class2"
        print(instance_var)


class Class3:
    var3 = "class3"


class1 = Class1()

class1.class2_to_class3()

print(class1.class3.var3)

Writing code like this works as desired, but I may not always want to use a static or class method decorator (see below), i.e., I may want to, within the same method, use the instance self.

class Class1:
    var = "class1"

    def class2_to_class3(self):
        Class2.from_class3(Class3)


class Class2:
    var2 = "class2"

    @staticmethod
    def from_class3(cls):
        cls.var3 += " appended by class2"
        print(cls.var3)
        return


class Class3:
    var3 = "class3"

    def hello(self):
        print("hello from class 3")


class1 = Class1()

class1.class2_to_class3()

print(Class3.var3)

This also works, but I don't exactly like the way the classes are instantiated "outside" Class1. Also, I dont like the fact that if I ask VScode to go to the definition to var3 from within def from_cass3, that it cant zoom to the location of var 3 because it is looking for instance.var3 instead of class3.var3.

class Class1:
    var = "class1"

    def class2_to_class3(self):
        class2.from_class3(class3)


class Class2:
    var2 = "class2"

    def from_class3(self, instance):
        instance.var3 += " appended by class2"


class Class3:
    var3 = "class3"


class1 = Class1()
class2 = Class2()
class3 = Class3()

print(class3.var3)
class1.class2_to_class3()
print(class3.var3)

Solution

  • Essentially, strings are immutable, meaning they can't be changed once they are created. In this part of your code:

    instance_var += " appended by class2"

    you are adding the string to the local variable instance_var. Strings aren't the only immutable objects, tuples are as well (this is the difference between tuples and lists). Your attempt only works for mutable objects like lists. For immutable objects, since you cannot modify them in place, you need a way to update the reference in the original object.

    As to why this is a bad idea, you are coupling your classes. This makes classes less modular and harder to maintain. You are also obscuring data flow, making it hard to track where data is modified. Good class design uses encapsulation, meaning each class is responsible for managing its own state.