Search code examples
pythonabstract-classsubclasssuperclassclass-variables

Access class variable value of a Python subclass that was defined in abstract superclass in implemented function within abstract superclass


I try to achieve the following:

  • Require class_variable to be "implemented" in ConcreteSubClass of AbstractSuperClass, i.e. make AbstractSuperClass.class_variable abstract
  • Define implemented (concrete) method in AbstractSuperClass which accesses the "implemented" value of ConcreteSubClass.class_variable

I would like to do this so that I won't have to implement method within all ConcreteSubClasses of AbstractSuperClass.

If I run the below code:

from abc import ABC


class AbstractSuperClass(ABC):
  class_variable: int
  
  def method(self):
    return self.instance_variable * AbstractSuperClass.class_variable
    

class ConcreteSubClass(AbstractSuperClass):
  class_variable: int = 2
  
  def __init__(self, instance_variable):
    self.instance_variable = instance_variable
    
concrete_subclass = ConcreteSubClass(instance_variable=2)
print(concrete_subclass.method())

The code fails with:

Traceback (most recent call last):
  File "<input>", line 19, in <module>
  File "<input>", line 8, in function
AttributeError: type object 'AbstractSuperClass' has no attribute 'class_variable'

Which is reasonable, because the value of class_variable is not assigned in AbstractSuperClass, but suspicious because AbstractSuperClass has attribute class_variable.

I would like to achieve the below:

def method(self):
    return self.instance_variable * <refer to cls of the concrete subclass this method will be called>.class_variable

How can I do this?


Solution

  • Based on @a_r's code, I came up with the solution where most of the code is in AbstractSuperClass and ConcreteSubClasses only need to define and implement minimal stuff:

    from abc import ABC
    
    class AbstractSuperClass(ABC):
      _class_variable: int
      
      @classmethod
      def class_variable(cls) -> int:
        return cls._class_variable
      
      def method(self):
        return self.instance_variable * self.class_variable()
        
    class ConcreteSubClass(AbstractSuperClass):
      _class_variable: int = 2
      
      def __init__(self, instance_variable):
        self.instance_variable = instance_variable
        
    concrete_subclass = ConcreteSubClass(instance_variable=2)
    print(concrete_subclass.method())