What is the correct way to extend a parent class method in modern Python

Question:

I frequently do this sort of thing:

class Person(object):
    def greet(self):
        print "Hello"

class Waiter(Person):
    def greet(self):
        Person.greet(self)
        print "Would you like fries with that?"

The line Person.greet(self) doesn’t seem right. If I ever change what class Waiter inherits from I’m going to have to track down every one of these and replace them all.

What is the correct way to do this is modern Python? Both 2.x and 3.x, I understand there were changes in this area in 3.

If it matters any I generally stick to single inheritance, but if extra stuff is required to accommodate multiple inheritance correctly it would be good to know about that.

Asked By: Gordon Wrigley

||

Answers:

Not sure if you’re looking for this but you can call a parent without referring to it by doing this.

super(Waiter, self).greet()

This will call the greet() function in Person.

Answered By: Ólafur Waage

You use super:

Return a proxy object that delegates
method calls to a parent or sibling
class of type. This is useful for
accessing inherited methods that have
been overridden in a class. The search
order is same as that used by
getattr() except that the type itself
is skipped.

In other words, a call to super returns a fake object which delegates attribute lookups to classes above you in the inheritance chain. Points to note:

  • This does not work with old-style classes — so if you are using Python 2.x, you need to ensure that the top class in your hierarchy inherits from object.
  • You need to pass your own class and instance to super in Python 2.x. This requirement was waived in 3.x.
  • This will handle all multiple inheritance correctly. (When you have a multiple inheritance tree in Python, a method resolution order is generated and the lookups go through parent classes in this order.)

Take care: there are many places to get confused about multiple inheritance in Python. You might want to read super() Considered Harmful. If you are sure that you are going to stick to a single inheritance tree, and that you are not going to change the names of classes in said tree, you can hardcode the class names as you do above and everything will work fine.

Answered By: Katriel

katrielalex’s answer is really the answer to your question, but this wouldn’t fit in a comment.

If you plan to go about using super everywhere, and you ever think in terms of multiple inheritance, definitely read the “super() Considered Harmful” link. super() is a great tool, but it takes understanding to use correctly. In my experience, for simple things that don’t seem likely to get into complicated diamond inheritance tangles, it’s actually easier and less tedious to just call the superclass directly and deal with the renames when you change the name of the base class.

In fact, in Python2 you have to include the current class name, which is usually more likely to change than the base class name. (And in fact sometimes it’s very difficult to pass a reference to the current class if you’re doing wacky things; at the point when the method is being defined the class isn’t bound to any name, and at the point when the super call is executed the original name of the class may not still be bound to the class, such as when you’re using a class decorator)

Answered By: Ben

I’d like to make it more explicit in this answer with an example. It’s just like how we do in JavaScript. The short answer is, do that like we initiate the constructor using super.

class Person(object):                                                             
   def __init__(self, name):                                                      
       self.name = name                                                           
                                                                                    
   def greet(self):                                                               
       print(f"Hello, I'm {self.name}")                                           
                                                                                    
class Waiter(Person):                                                             
    def __init__(self, name):                                                     
        super().__init__(name)
        # initiate the parent constructor
        # or super(Waiter, self).__init__(name)                                                    
                                                                                    
    def greet(self):                                                              
        super(Waiter, self).greet()                                               
        print("Would you like fries with that?")                                  
                                                                                    
waiter = Waiter("John")                                                           
waiter.greet()

# Hello, I'm John
# Would you like fries with that?
Answered By: Lerner Zhang
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.