can a subclass method determine if called by superclass?

P

Peter

Situation: I am subclassing a class which has methods that call other
class methods (and without reading the code of the superclass I am
discovering these by trial and error as I build the subclass - this is
probably why I may have approached the problem from the wrong
viewpoint :)).

Problem: when overriding one of these "indirectly called" superclass
methods I would like to take differing actions (in the subclass
instance) depending on whether it is the superclass or the subclass
instance performing the call.

Question: Is there any way to determine in a method whether it is
being called by the superclass or by a method of the subclass
instance?

Now I suspect that what I am doing is actually very muddy thinking :)
and I don't want to attempt to explain why I am approaching the design
this way as an explanation would require too much work - I will
consider an alternative inheritance approach while waiting an answer,
but the answer to the question interested me (even if I do a redesign
and come up with a more "elegant" approach to the problem).

Thanks
Peter
 
I

Ian Kelly

Situation: I am subclassing a class which has methods that call other
class methods (and without reading the code of the superclass I am
discovering these by trial and error as I build the subclass - this is
probably why I may have approached the problem from the wrong
viewpoint :)).

Problem: when overriding one of these "indirectly called" superclass
methods I would like to take differing actions (in the subclass
instance) depending on whether it is the superclass or the subclass
instance performing the call.

Question: Is there any way to determine in a method whether it is
being called by the superclass or by a method of the subclass
instance?

Well, you could get the previous stack level using
traceback.extract_stack() and check the filename. But it sounds like
what you actually have are two different methods -- one that is used
by the superclass, and one that only the subclass knows about and
uses. Why not implement it as such?
 
P

Peter

Well, you could get the previous stack level using
traceback.extract_stack() and check the filename.  But it sounds like
what you actually have are two different methods -- one that is used
by the superclass, and one that only the subclass knows about and
uses.  Why not implement it as such?

Thanks Ian - that is one possibility.

I am trying to create a subclass with slightly different functionality
and use it with an existing code base i.e. there is already one or
more modules that instantiate the current superclass and I want to
just drop in this new class to replace it with no ripples up the line
(so to speak). The new class implements some interface changes that
can safely be hidden from the rest of the application.
 
S

Steven D'Aprano

I am trying to create a subclass with slightly different functionality
and use it with an existing code base i.e. there is already one or
more modules that instantiate the current superclass and I want to
just drop in this new class to replace it with no ripples up the line
(so to speak). The new class implements some interface changes that
can safely be hidden from the rest of the application.

This is *exactly* the idea behind subclassing. I don't understand your
problem, can you explain more?

If you want to change behaviour of an object, you subclass it, then
override or overload the methods you want to change. You certainly
shouldn't be changing the superclass to recognise when it is being called
from a subclass! That's completely the wrong approach -- you should put
all the new behaviour in the new class.


# WRONG! Don't do this.

class Parent(object):
def method(self, arg):
if type(self) is not Parent:
# Must be a subclass.
print("Called from a subclass. But which one?")
print("Doing method stuff.")

class Child(Parent):
pass


# RIGHT! Do this instead.

class Parent(object):
def method(self, arg):
print("Doing method stuff.")

class Child(Parent):
def method(self, arg):
# Overload an existing method.
print("Called from Child subclass.")
super().method(arg) # Python 3 only
# Python 2 use: super(Child, self).method(arg)


If method() returns a result, you can capture the result of calling the
superclass method and then modify it as needed. Or you can override the
method completely, and not call the parent method() at all.

Now you can use Child() anywhere that you can use Parent and the caller
shouldn't even notice. Or at least that is the idea behind subclassing,
although it is possible to break it. You will be okay if the caller uses
duck-typing, or isinstance checks, but not if they do exact type checks
(which is almost always the wrong thing to do).

c = Child()

# Duck-typing works:
hasattr(c, 'method') # returns True

# So do isinstance checks:
isinstance(c, Parent) # returns True

# but this doesn't and defeats the purpose of having subclasses:
type(c) is Parent # returns False, you shouldn't do this!


If the caller does do exact type checks, then almost certainly it should
be considered a bug in their code and they should be beaten with a clue-
bat and told to use isinstance (good) or duck-typing (better still).


If I have misunderstood your problem, can you give a concrete (but
simple) example of what you are trying to do?
 
J

Jean-Michel Pichavant

Peter said:
Situation: I am subclassing a class which has methods that call other
class methods (and without reading the code of the superclass I am
discovering these by trial and error as I build the subclass - this is
probably why I may have approached the problem from the wrong
viewpoint :)).

Problem: when overriding one of these "indirectly called" superclass
methods I would like to take differing actions (in the subclass
instance) depending on whether it is the superclass or the subclass
instance performing the call.

Question: Is there any way to determine in a method whether it is
being called by the superclass or by a method of the subclass
instance?

Now I suspect that what I am doing is actually very muddy thinking :)
and I don't want to attempt to explain why I am approaching the design
this way as an explanation would require too much work - I will
consider an alternative inheritance approach while waiting an answer,
but the answer to the question interested me (even if I do a redesign
and come up with a more "elegant" approach to the problem).

Thanks
Peter
As you suspected, this is probably the wrong approach.

However since you asked for a solution anyway :eek:)

class Parent(object):
def foo(self):
# implementation by subclasses is still REQUIRED
if self.__class__ is Parent:
raise NotImplementedError()
# common code for all foo methods
print "calling foo"

class Child(Parent):
def foo(self):
# You can still call the virtual method which contains some code
Parent.foo(self)
# here the custom code


p = Parent()
c = Child()


c.foo()
p.foo()

Note that this is not the best approach, still acceptable because there
is no code specific to a subclass in the base class.

JM
 
J

Jean-Michel Pichavant

Jean-Michel Pichavant said:
As you suspected, this is probably the wrong approach.

However since you asked for a solution anyway :eek:)

class Parent(object):
def foo(self):
# implementation by subclasses is still REQUIRED
if self.__class__ is Parent:
raise NotImplementedError()
# common code for all foo methods
print "calling foo"

class Child(Parent):
def foo(self):
# You can still call the virtual method which contains some code
Parent.foo(self)
# here the custom code


p = Parent()
c = Child()


c.foo()
p.foo()

Note that this is not the best approach, still acceptable because
there is no code specific to a subclass in the base class.

JM
I just realized I didn't addressed the problem you described, sorry,
just ignore my mail.

JM
 
P

Peter

Thanks for the help guys - it appears there is no better way than Ian
suggested.

I have restructured my class hierarchy so the problem has now "gone
away".

It would have been nice to discover some previously unknown (to me at
least!) Python trick, but such is life :)

Regards
Peter
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,981
Messages
2,570,188
Members
46,732
Latest member
ArronPalin

Latest Threads

Top