Pavel Krutikhin
Pavel Krutikhin

Reputation: 53

How to change variable of main class from inherited class?

I need to change object variable directly from inherited class. Here is my code example:

class A(object):
    def __init__(self,initVal=0):
        self.myVal = initVal

    def worker(self):
        self.incrementor = B()
        self.incrementor.incMyVal(5)        

class B(A):
    def incMyVal(self,incVal):
        super().myVal += incVal

obj = A(5)
print(obj.myVal)

obj.worker()
print(obj.myVal)

But it doesn't work:

AttributeError: 'super' object has no attribute 'myVal'

I also tried to use global/nonlocal keywords with my variable in B class, but no luck.

In my main case, the B class is an event handler. And it should change the attribute of an object when an event fires. So I'm not able to use return in the incMyVal method.

Upvotes: 0

Views: 2047

Answers (1)

Martijn Pieters
Martijn Pieters

Reputation: 1121744

super() can only search for class attributes in the class MRO, not instance attributes. myVal is set on an instance of the class, not on a class itself.

There is only ever one instance; it doesn't matter if code from class A or a derived class is altering attributes on an instance, it is just one namespace.

However, in your case, you shouldn't even be using inheritance. You are trying to use an independent, second instance to alter the attributes of an instance of A. Class inheritance doesn't give you access to instances of the base class like this.

Refactor B to take an instance of A, then act on that instance:

class B:
    def __init__(self, ainstance):
        self.ainstance = ainstance
    def incMyVal(self, incVal):
        self.ainstance.myVal += incVal

Note that B is not a subclass of A here; it is not a (specialised) object of the same type at all; it is a different kind of thing, something that increments attributes of another object.

Pass in the instance when you create an instance of B:

def worker(self):
    self.incrementor = B(self)
    self.incrementor.incMyVal(5)        

This does create a circular reference, which can keep objects alive for longer than perhaps needed. You may want to use a weak reference instead:

import weakref

class B:
    def __init__(self, ainstance):
        self.ainstance_ref = weakref.ref(ainstance)
    def incMyVal(self, incVal):
        ainstance = self.ainstance_ref()
        if ainstance is not None:
            ainstance.myVal += incVal

Now B instances only hold a weak reference to their A instance, and will do nothing if that instance no longer exists.

Upvotes: 3

Related Questions