0

I have a base class which is abstract. However, every object of this type will hold a value val.

In the derived class, I will then have an __init__ function. This function does NOT take the value val as input, it just knows it by definition.

class Base(ABC):
    pass

class Derived(Base):
    def __init__(self):
        self.val = 5

All sub-classes of Base will have this val, and they will all set it internally, i.e. it won't be an argument. However now if I am using inheritance, nobody knows that Base-objects have val.

So how am I meant to structure this code, so that users now that if its a Base-object, it will have val. Do I use class variables somehow? Or do I need to give Base an __init__ even though it is an abstract method? And how should that __init__ be structured, given that derived classes don't take any parameters in their __init__?

nobita
  • 3
  • 2

2 Answers2

0

If I understand correctly you want val to be an attribute of base that is inherited by its sub-classes where it is assigned a value?

class Base:
    # val is now a class attribute of base
    val = None

class Derived(Base):

    def __init__(self):

        self.val = 5

Class attributes are available at the class level so they do not require an instance (self) to create them. However, they are accessed within classes by the form self.attribute

BeanBagTheCat
  • 435
  • 4
  • 7
0

Use a descrpitor that doesn't allow access from the base class.

from abc import ABC
from weakref import WeakKeyDictionary

class Abstract_attr:
    def __init__(self, name, cls_name):
        self.name = name
        self.cls_name = cls_name
        self.data = WeakKeyDictionary()
    def __get__(self, instance, owner):
        if instance not in self.data:
            raise NotImplementedError('NOT ACCESSIBLE FROM BASE CLASS')
        return self.data.get(instance, None)
    def __set__(self, instance, val):
        print(self, instance.__class__.__name__)
        if self.cls_name == instance.__class__.__name__:
            raise NotImplementedError('CANNOT SET ON BASE CLASS')
        self.data[instance] = val
        
class Base(ABC):
    val = Abstract_attr('val','Base')

class Derived(Base):
    def __init__(self):
        self.val = 5

print(Derived().val)
print('val' in dir(Base))
Base().val

5
True
Traceback (most recent call last):
  File "c:/pyfoo/tmp.py", line 57, in <module>
    Base().val
  File "c:/pyfoo/tmp.py", line 40, in __get__
    raise NotImplementedError('NOT ACCESSIBLE FROM BASE CLASS')
NotImplementedError: NOT ACCESSIBLE FROM BASE CLASS

If you need hasattr to work just return something unusable instead of raising an error.

class Abstract_attr:
    def __init__(self, name, cls_name):
        self.name = name
        self.cls_name = cls_name
        self.data = WeakKeyDictionary()
    def __get__(self, instance, owner):
        if instance not in self.data:
            return 'NOT ACCESSIBLE FROM BASE CLASS'
        else:
            return self.data.get(instance, None)
    def __set__(self, instance, val):
        # print(self, instance.__class__.__name__)
        if self.cls_name == instance.__class__.__name__:
            return 'CANNOT SET ON BASE CLASS'
        else:
            self.data[instance] = val
wwii
  • 23,232
  • 7
  • 37
  • 77