I came accross a piece of Python legacy code at work that I couldn't understand how it could work without errors. Obviously I can't write the exact code here but here is a minimal working example:
class ClassB:
def func(self, txt: str):
return self.str_to_uppercase(txt)
class ClassA(ClassB):
def str_to_uppercase(self, txt: str):
return txt.upper()
if __name__ == "__main__":
my_instance = ClassA()
print(my_instance.func("Hello, World!"))
stdout: HELLO, WORLD!
What's strange to me is that, although ClassB
is not directly inheriting from ClassA
where the instance method str_to_uppercase()
is defined, ClassB
is still able to call this method. I should also note that my linter (pylint) is complaining that str_to_uppercase()
is not defined in ClassB
. So I'm struggling to understand how the mechanics of the code works here regarding inheritence.
Secondly, this code looks strange to me. It doesn't seem very "Pythonic". So, as a second question, I was wondering in which usecases such code is useful?
CodePudding user response:
This is quite standard in OO and even has a name: The Template Method Pattern.
The idea is that ClassB
should define some useful functionality, but only at a high level. Its code calls other methods that it doesn't define, but still depends on.
This leaves other classes (ClassA
in your example) to provide the fine details, called implementation details.
CodePudding user response:
When a method is called on an object, Python accesses the list of defined methods of the actual class of the object. Inside the ClassB.func(self,...)
method, the actual class of the self
object is ClassA
. ClassA has a str_to_uppercase()
so it can be called even though this code is part of ClassB
(which doesn't need to know ClassA
).
If you passed my_instance
to any regular function that expects an object as parameter, you would be able to call the str_to_uppercase()
method of that object within the function (and that would not be a surprise). The name self
in method definitions is actually just a convention for the first parameter of the function which is implicitly the instance of the object calling it.