Questions about parent class calls function of child class

Hi,

For the following example code, parent class PPC uses slef.forward(x) to call the function of child class.

I couldn’t understand the following questions:

  1. forward is not a virtual function, how could parent class call it? what is the PEP link about my question? is there any explanation of python language?
  2. how does parent class find the forward? via which dict or list?
  3. in my example code, if I use abstractmethod, should it be a better syntax?
class PPC():
    def kkfunc(self):
        print('kk func in parent class')

    def __call__(self, x):
        xx= self.forward(x)
        # print('call function === ', x)
        return xx

class PPSub(PPC):
    def __init__(self):
        pass

    def pfunc(self):
        print('in sub class')
    def forward(self, x):
        print('forward function in ppsub class===', x)
        return x+1


ps1 = PPSub()
xxx = ps1(9)

For the following example code, parent class PPC uses slef.forward(x)
to call the function of child class.

I couldn’t understand the following questions:

  1. forward is not a virtual function, how could parent class call it? what is the PEP link about my question? is there any explanation of python language?

Python doesn’t have virtual methods.

  1. how does parent class find the forward? via which dict or list?

It doesn’t. This will only work in an instance of PPSub.

  1. in my example code, if I use abstractmethod, should it be a better syntax?

Better style and practice, definitely. Syntax is syntax. If its legal
syntax, you’re good.

Let’s look at this more closely.

PPC is an abstract class. It uses .forward(), but doesn’t provide one.
If you instantiate an instance of PPC, call won’t work.

PPSub is a concrete subclass of PPC: it supplies a .forward()
implementation. If you make an instance of PPSub, its call method
will work.

When you call ps1(9) it goes to the call method. PPSub doesn’t
implement this, but its superclass does. So that gets used. That method
in turn calls .forward. But it finds that from self, which is an
instance of PPSub, and PPSub does implement .forward.

These days Python has support for making abstract classes more
rigorously. Let’s update your code using it:

from abc import ABC, abstractmethod

@ABC
class PPC():

    def kkfunc(self):
        print('kk func in parent class')

    def __call__(self, x):
        xx= self.forward(x)
        # print('call function === ', x)
        return xx

    @abstractmethod
    def forward(self, x):
        raise NotImplementedError("no .forward method")

class PPSub(PPC):
    def __init__(self):
        pass

    def pfunc(self):
        print('in sub class')

    def forward(self, x):
        print('forward function in ppsub class===', x)
        return x+1

ps1 = PPSub()
xxx = ps1(9)

This doesn’t change how any of the classes work, but the @ABC and
@abstractmethod decorator arrange that you can’t actually make an
instance of PPC. The @ABC decorator makes the class check that none of
its methods are abstractmethods.

PPSub will inherit that behaviour, but since it provides a .forward
which is not an abstractmethod, it has no abstractmethods and you’re
allowed to make one.

Cheers,
Cameron Simpson cs@cskk.id.au

Many thanks for your professional and detailed answers.
I would like to try to understand your comments and add a few more questions:

  1. the syntax in my example code is fine. Parent class will not find the function forward as the ‘self’ of self.forward refers to instance of child class, but not parent class.
  2. python has new abstract class and method to solve the question I am experiencing.
  3. the @ABC decrator is the same as: class PPC(metaclass=abc.ABCMeta) , is my understanding correct?
  4. @abstractmethod is similar as virtual function in C++, right?
  5. when was the abstract class and method feature added to python? it seems that abstract class was supported long time ago
  6. do you have any link or doc about the explanation of abstract class and method in python language?

I find it helpful to think of it like this: the child class inherits __call__, so when you call the __call__ method of instances of the child class, it then use the forward method of the same instance. This is all made apparent with the self argument.

1 Like

Yes, I have the same understanding as yours.

Many thanks for your professional and detailed answers.
I would like to try to understand your comments and add a few more questions:

  1. the syntax in my example code is fine.

Did the python interpreter accept it? Syntax ok!

Parent class will not find the function forward as the ‘self’ of
self.forward refers to instance of child class, but not parent class.

Yes. Looking up a method on a class instance tries the class itself then
the superclasses in the method resolution order (from
type(instance).mro if you want to look at it).

  1. python has new abstract class and method to solve the question I am experiencing.

Yes, the “abc” standard module.

  1. the @ABC decrator is the same as: class PPC(metaclass=abc.ABCMeta) , is my understanding correct?

I think so.

  1. @abstractmethod is similar as virtual function in C++, right?

Yes. Though my C++ is weak. The idea is to present the method as being a
required method, but indicate that this class does not implement it.

  1. when was the abstract class and method feature added to python?
  2. do you have any link or doc about the explanation of abstract class
    and method in python language?

Python 3 I think. Module docs here:

https://docs.python.org/3/library/abc.html#module-abc

Various bits added in particular releases of Python as indicated by
notes like “New in version 3.4.”.

Cheers,
Cameron Simpson cs@cskk.id.au

Thank you Cameron!

I modified the code as follows.
the printed messages are:

self in PPSub forward function:  <__main__.PPSub object at 0x00000114D3935820>
self in PPC call function:  <__main__.PPSub object at 0x00000114D3935820>

Obviously, call function is inherited by PPSub class, and forward function is run in the instance of PPSub but not parent class.

I don’t know how to do what you said above. Could you please make it clearer?

class PPC():
    def kkfunc(self):
        print('kk func in parent class')

    def __call__(self, x):
        xx= self.forward(x)
        # print('call function === ', x)
        print('self in PPC call function: ', self)
        return xx

class PPSub(PPC):
    def __init__(self):
        pass

    def pfunc(self):
        print('in sub class')
    def forward(self, x):
        print('forward function in ppsub class===', x)
        print('self in PPSub forward function: ', self)
        return x+1

ps0 = PPC()

ps1 = PPSub()
xxx = ps1(9)

The cursed markdown formatting.

print(PPSub.__mro__)

That is dot underscore underscore “m” “r” “o” underscore underscore.

Cheers,
Cameron Simpson cs@cskk.id.au

Got it. Thank you Cameron!