Pythonist
Pythonist

Reputation: 2679

Why does the Python's inheritance mechanism allow me to define classes that do not get properly instantiated?

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?

Upvotes: 0

Views: 55

Answers (1)

kindall
kindall

Reputation: 184345

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.

Upvotes: 1

Related Questions