0

I'm sorry for the rather vague formulation of the question. I'll try to clarify what I mean through a simple example below. I have a class I want to use as a base for other classes:

class parent:
    def __init__(self, constant):
        self.constant = constant
    def addconstant(self, number):
        return self.constant + number

The self.constant parameter is paramount for the usability of the class, as the addconstant method depends on it. Therefore the __init__ method takes care of forcing the user to define the value of this parameter. So far so good.

Now, I define a child class like this:

class child(parent):
    def __init__(self):
        pass

Nothing stops me from creating an instance of this class, but when I try to use the addconstant, it will obviously crash.

b = child()
b.addconstant(5)
AttributeError: 'child' object has no attribute 'constant'

Why does Python allow to do this!? I feel that Python is "too flexible" and somehow breaks the point of OOP and encapsulation. If I want to extend a class to take advantage of inheritance, I must be careful and know certain details of the implementation of the class. In this case, I have to know that forcing the user to set the constant parameter constant is fundamental not to break the usability of the class. Doesn't this somehow break the encapsulation principle?

Pythonist
  • 1,937
  • 1
  • 14
  • 25
  • 2
    Python puts a lot of onus on the user to be a “responsible adult”, e.g. with barely protected “private” attributes and such. It’s not there to babysit you… – deceze Feb 19 '21 at 19:20
  • 2
    A lot of the reason people love Python is that it is so flexible and doesn't really disallow many things. So if you want to call the parent initializer, then call it, if you don't, then don't. – wim Feb 19 '21 at 19:21
  • 2
    "If I want to extend a class to take advantage of inheritance, I must be careful and know certain details of the implementation of the class" exactly! – Sayandip Dutta Feb 19 '21 at 19:21
  • Encapsulation is more of a Java thing, not so much for Python. – M-Chen-3 Feb 19 '21 at 19:22
  • 2
    Also note that the class definition itself does not define the `constant` attribute; calling `parent.__init__` does. – chepner Feb 19 '21 at 19:23

1 Answers1

1

Because Python is a dynamic language. It doesn't know what attributes are on a parent instance until parent's initializer puts them there. In other words, the attributes of parent are determined when you instantiate parent and not an instant before.

In a language like Java, the attributes of parent would be established at compile time. But Python class definitions are executed, not compiled.

In practice, this isn't really a problem. Yes, if you forget to call the parent class's initializer, you will have trouble. But calling the parent class's initializer is something you pretty much always do, in any language, because you want the parent class's behavior. So, don't forget to do that.

A sometimes-useful technique is to define a reasonable default on the class itself.

class parent:
    constant = 0
    def __init__(self, constant):
        self.constant = constant
    def addconstant(self, number):
        return self.constant + number

Python falls back to accessing the class's attribute if you haven't defined it on an instance. So, this would provide a fallback value in case you forget to do that.

kindall
  • 178,883
  • 35
  • 278
  • 309
  • 2
    I would argue that the class default fallback just obscures the error. Now you won’t see an error message, but it’ll probably behave differently in subtle ways. – deceze Feb 19 '21 at 19:30
  • Depends on the use case, but yeah, I can see favoring "fail fast" here. – kindall Feb 19 '21 at 19:31