Python: how to inherit and override

17,821

Solution 1

How you construct an object of subclass B "based on" one of class A depends exclusively on how the latter keeps state, if any, and how do you best get to that state and copy it over. In your example, instances of A are stateless, therefore there is absolutely no work you need to do in B's '__init__'. In a more typical example, say:

class A(object):
   def __init__(self):
     self._x = 23
     self._y = 45
   def f(self):
      print 'in f,', self._x
   def h(self):
      print 'in h,', self._y

the state would be in the two instance attributes _x and _y, so those are what you need to copy over:

class B(A):
     def __init__(self, a):
        self._x = a._x
        self._y = a._y

     def f(self):
         print 'in B->f,', self._x

This is the most common and normal approach, where the subclass accepts and directly implements its state-dependence on the superclass -- it's very straightforward and linear.

You normally look for A's instance state aspects in A's '__init__', because most normal, straightforward Python code establishes instance state at initialization (attributes might be added and removed later, or even from code outside of the class's body, but that's not common and generally not advisable).

It is possible to add a little touch of "magic" (introspection-based programming), e.g...:

class B1(A):
    def __init__(self, a):
        try: s = a.__getstate__()
        except AttributeError: s = a.__dict__
        try: self.__setstate__(s)
        except AttributeError: self.__dict__.update(s)

getstate is a special method that classes may define -- if they do, it's used (e.g. by pickling) to "get the state" of their instances for serialization purpose (otherwise, the instance's __dict__ is deemed to be the instance's "state"). It may return a dict (in which case the .update call updates self's state), but it may also return anything else if the class also defines a __setstate__ that accepts it (so this code tries that route first, before falling back to the update possibility). Note that in this use case either or both of the special methods would be inherited from A -- I wouldn't define / override them in B (unless there are further subtle goals to be achieved that way of course;-).

Is it worth using these four lines of "magic" in lieu of the simple assignments I first suggested? Mostly, no -- simplicity is preferable. But if A does anything special or is subject to external code altering its state, this solution can be more powerful and general (that's what you're buying by accepting its complication). So, you have to know if the latter case applies (and then "go for the big guns" of the special state-related methods), or if A and its instances are "pretty normal vanilla ones", in which case I would strongly recommend choosing simplicity and clarity instead.

Solution 2

Try this:

class A:
  def f(self):
    print("in f")

  def h(self):
    print("in h")

class B(A):
  def f(self):
    print("in B:f")

def test(x):
  x.f()
  x.h()

test(A())
test(B())

Note, I'm using Python 3, which is the reason for print taking the arguments in parenthesis.

Output:

in f
in h
in B:f
in h

Solution 3

You need to put the self argument into the argument list for instance methods in python.

Once you've done that, it will just work, because all methods are virtual in python.

Share:
17,821
Guy
Author by

Guy

Updated on June 23, 2022

Comments

  • Guy
    Guy almost 2 years

    Consider this situation:

    I get an object of type A which has the function f:

    class A:
       def f(self):
          print 'in f'
       def h(self):
          print 'in h'
    

    and I get an instance of this class, but I want to override the f function, yet save the rest of the functionality of A. So what I was thinking was something of the sort:

    class B(A):
         def __init__(self, a):
            #something here
         ....
    
         def f(self):
             print 'in B->f'
    

    and the usage would be:

    def main(a):
       b = B(a)
       b.f()   #prints "in B->f"
       b.h()   #print "in h"
    

    What I want is a sort of copy constructor that gets a parent of the current class (A), and returns an instance of this class (B).

    How do you do such a thing? How would the __init__ method look?

    Note: this post has been edited by the original poster to incorporate changes suggested in the comments, which is why some of the suggestions look redundant or incorrect.