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?

2

There are 2 best solutions below

0
bugfoot On BEST ANSWER

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())
1
r_a On

Please try the below code. I hope it works :

from abc import ABC, abstractmethod
class AbstractSuperClass(ABC):
@property
@classmethod
@abstractmethod
def class_variable(cls):
    pass

def method(self):
    return self.instance_variable * self.class_variable

class ConcreteSubClass(AbstractSuperClass):
class_variable = 2

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

@classmethod
def class_variable(cls):
    return cls.class_variable

concrete_subclass = ConcreteSubClass(instance_variable=2)
    print(concrete_subclass.method())