This base class has a method, calc_func(self), which uses uniformly named methods, func(self), in the child classes. This works but this 'architecture' would be very hard to follow if the code ever grew more complex.
No, that's exactly how object-oriented polymorphism is supposed to work. You're already doing it right!
You say that this seems difficult to understand, but it's not clear why you think it's difficult. Probably the only thing you could do is (a) mark the base class as abstract (b) have all methods that child classes are supposed to supply exist on the base class, but only have them raise NotImplementedError
.