asdasasdsa
asdasasdsa

Reputation: 1481

Python Method overriding, does signature matter?

Lets say I have

class Super():
  def method1():
    pass

class Sub(Super):
  def method1(param1, param2, param3):
      stuff

Is this correct? Will calls to method1 always go to the sub class? My plan is to have 2 sub classes each override method1 with different params

Upvotes: 148

Views: 108836

Answers (6)

phpjunkie
phpjunkie

Reputation: 195

does signature matter?

No it doesn't but PyCharm will generate a warning when the overridden method doesn't match the base method.
Signature of method 'Baz.method1()' does not match signature of the base method in class 'Foo'

You can get around this warning by adding typing.Optional as a type hint. You can also use locals() to call the base method if all the parameters are None.

from typing import Any, Optional
from inspect import signature


class Foo(object):
    def __init__(self):
        pass

    def method1(self):
        print('foo')


class Baz(Foo):
    def __init__(self):
        super(Foo, self).__init__()

    def method1(self, param1: Optional[str] = None, param2: Optional[Any] = None, param3: Optional[Any] = None):
        parameters, copy_of_locals = signature(self.method1).parameters, locals().copy()
        if not next((param for param in parameters if copy_of_locals[param] is not None), None):
            super().method1()
            return
        print('baz')

Output:
baz = Baz()
baz.method1()
>>> 'foo'
baz.method1(param1 = 'dcdb')
>>> 'baz'

Upvotes: 2

Shital Shah
Shital Shah

Reputation: 68818

In Python, methods are just key-value pairs in the dictionary attached to the class. When you are deriving a class from a base class, you are essentially saying that method name will be looked into first derived class dictionary and then in the base class dictionary. In order to "override" a method, you simply re-declare the method in the derived class.

So, what if you change the signature of the overridden method in the derived class? Everything works correctly if the call is on the derived instance but if you make the call on the base instance, you will get an error because the base class uses a different signature for that same method name.

There are however frequent scenarios where you want derived class method have additional parameters and you want method call work without error on base as well. This is called "Liskov substitution principle" (or LSP) which guarantees that if person switches from base to derived instance or vice versa, they don't have to revamp their code. To do this in Python, you need to design your base class with the following technique:

class Base:
    # simply allow additional args in base class
    def hello(self, name, *args, **kwargs):
        print("Hello", name)

class Derived(Base):
      # derived class also has unused optional args so people can
      # derive new class from this class as well while maintaining LSP
      def hello(self, name, age=None, *args, **kwargs):
          super(Derived, self).hello(name, age, *args, **kwargs) 
          print('Your age is ', age)

b = Base()
d = Derived()

b.hello('Alice')        # works on base, without additional params
b.hello('Bob', age=24)  # works on base, with additional params
d.hello('Rick')         # works on derived, without additional params
d.hello('John', age=30) # works on derived, with additional params

Above will print:

    Hello Alice
    Hello Bob
    Hello Rick
    Your age is  None
    Hello John
    Your age is  30
. Play with this code

Upvotes: 170

Zitrax
Zitrax

Reputation: 20304

You could do something like this if it's ok to use default arguments:

>>> class Super():
...   def method1(self):
...     print("Super")
...
>>> class Sub(Super):
...   def method1(self, param1="X"):
...     super(Sub, self).method1()
...     print("Sub" + param1)
...
>>> sup = Super()
>>> sub = Sub()
>>> sup.method1()
Super
>>> sub.method1()
Super
SubX

Upvotes: 4

quasistoic
quasistoic

Reputation: 4687

It will work:

>>> class Foo(object):
...   def Bar(self):
...     print 'Foo'
...   def Baz(self):
...     self.Bar()
... 
>>> class Foo2(Foo):
...   def Bar(self):
...     print 'Foo2'
... 
>>> foo = Foo()
>>> foo.Baz()
Foo
>>> 
>>> foo2 = Foo2()
>>> foo2.Baz()
Foo2

However, this isn't generally recommended. Take a look at S.Lott's answer: Methods with the same name and different arguments are a code smell.

Upvotes: 2

Ignacio Vazquez-Abrams
Ignacio Vazquez-Abrams

Reputation: 799082

Python will allow this, but if method1() is intended to be executed from external code then you may want to reconsider this, as it violates LSP and so won't always work properly.

Upvotes: 53

Zaur Nasibov
Zaur Nasibov

Reputation: 22659

In python, all class methods are "virtual" (in terms of C++). So, in the case of your code, if you'd like to call method1() in super class, it has to be:

class Super():
    def method1(self):
        pass

class Sub(Super):
    def method1(self, param1, param2, param3):
       super(Sub, self).method1() # a proxy object, see http://docs.python.org/library/functions.html#super
       pass

And the method signature does matter. You can't call a method like this:

sub = Sub()
sub.method1() 

Upvotes: 2

Related Questions