0

I have a Python class like this:

class A:
  __val1: float = 0.0
  __val2: float

  def __init__():
     validate()

  def validate() -> bool:
     if not hasattr(self, __val2): # how is this supposed to be done?
         raise NotImplementedError()
     return self.__val1 >= self.val2        

My goal is to use A as an abstract class where the child classes are forced to implement __val2.

I create a child class B:

class B(A):
  __val2 = 1.0
  
  def __init__():
     super().__init__()

When I initialize an object of class B, this error is thrown:

E   AttributeError: 'B' object has no attribute '_A__val2'

I tried modifying B like this:

class B(A):
  A.__val2 = 1.0
  [...]

But this throws the same exception.

Trying the same but with super:

class B(A):
  super.__val2 = 1.0
  [...]

But this throws another error:

E   TypeError: can't set attributes of built-in/extension type 'super'

What is the Pythonic way do handle this kind of abstraction? How should validate() be implemented so that it checks for __val2?

Carsten
  • 1,912
  • 1
  • 28
  • 55
  • 2
    See https://stackoverflow.com/questions/1301346/what-is-the-meaning-of-single-and-double-underscore-before-an-object-name – Thierry Lathuille Jul 28 '20 at 15:31
  • 1
    Not sure what your are suggesting. Renaming `__val2` to `val2` in all places does not make a difference. Neither does using the notation `_A__val2` (unless I misunderstand where you suggest to apply it). – Carsten Jul 28 '20 at 15:38
  • 1
    You have an incorrectly defined class. `self` is not defined anywhere, and you appear to be confusing class attributes with instance attributes. – chepner Jul 28 '20 at 15:59

1 Answers1

1

When you use double preceding underscores for your class attribute names, python converts them to _<classname>_<varname>. So in your case, A.__val2 becomes A._A__val2 and B.__val2 becomes B._B__val2, which means they won't share the same name. See the link Thierry Lathuille has shared in the comment on your question, and here's the link to python docs: https://docs.python.org/3/tutorial/classes.html#private-variables.

You can fix this by not using double underscores, so go for something like _val2 or val2.

There are a few other issues in your code: all your class methods need self as the first argument, and you need to access them by calling self.<method_name>. So to access validate, you need to call it with self.validate(). Also, the second arg to hasattr must be a string.

Here's an updated example that should fix your issue:

class A:
  _val1: float = 0.0
  _val2: float

  def __init__(self):
     self.validate()

  def validate(self) -> bool:
     if not hasattr(self, '_val2'):
         raise NotImplementedError()
     return self._val1 >= self._val2

class B(A):
  _val2 = 1.0

  def __init__(self):
     super().__init__()

print(B()._val2)
kaveh
  • 2,046
  • 1
  • 21
  • 33