Methods with the same name in one class in Python

PythonOverloading

Python Problem Overview


How can I declare a few methods with the same name, but with different numbers of parameters or different types in one class?

What must I change in the following class?

class MyClass:
    """"""

    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
    def my_method(self,parameter_A_that_Must_Be_String):
        print parameter_A_that_Must_Be_String

    def my_method(self,parameter_A_that_Must_Be_String,parameter_B_that_Must_Be_String):
        print parameter_A_that_Must_Be_String
        print parameter_B_that_Must_Be_String

    def my_method(self,parameter_A_that_Must_Be_String,parameter_A_that_Must_Be_Int):
        print parameter_A_that_Must_Be_String * parameter_A_that_Must_Be_Int

Python Solutions


Solution 1 - Python

You can have a function that takes in a variable number of arguments.

def my_method(*args, **kwds):
    # Do something

# When you call the method
my_method(a1, a2, k1=a3, k2=a4)

# You get:
args = (a1, a2)
kwds = {'k1':a3, 'k2':a4}

So you can modify your function as follows:

def my_method(*args):
    if len(args) == 1 and isinstance(args[0], str):
        # Case 1
    elif len(args) == 2 and isinstance(args[1], int):
        # Case 2
    elif len(args) == 2 and isinstance(args[1], str):
        # Case 3

Solution 2 - Python

You can't. There are not overloads or multimethods or similar things. One name refers to one thing. As far as the language is concerned anyway, you can always emulate them yourself... You could check types with isinstance (but please do it properly - e.g. in Python 2, use basestring to detect both strings and unicode), but it's ugly, generally discouraged and rarely useful. If the methods do different things, give them different names. Consider polymorphism as well.

Solution 3 - Python

Using Python 3.5 or higher, you can use @typing.overload to provide type annotations for overloaded functions/methods.

From the docs:

@overload
def process(response: None) -> None:
    ...
@overload
def process(response: int) -> tuple[int, str]:
    ...
@overload
def process(response: bytes) -> str:
    ...
def process(response):
    <actual implementation>

Solution 4 - Python

Short answer: you can't (see this previous discussion). Typically you'd use something like (you could add more type checking and reorder):

def my_method(self,parameter_A, parameter_B=None):
  if isinstance(parameter_B, int):
    print parameter_A * parameter_B
  else:
    print parameter_A
    if parameter_B is not None:
      print parameter_B

Solution 5 - Python

You can try multimethods in Python:

http://www.artima.com/weblogs/viewpost.jsp?thread=101605

But I don't believe multimethod is a way to go. Rather objects that you pass to a method should have common interface. You are trying to achieve method overloading similar to the one in C++, but it is very rarely required in Python. One way to do this is a cascade of ifs using isinstance, but that's ugly.

Solution 6 - Python

Python is nothing like Java.

There are not really types, just objects with methods.

There is a way to test if a passed object is from a class, but it is mainly bad practices.

However, the code you want to produce for the two first methods should be something like

class MyClass(object):
    def my_method(self, str1, str2=None):
        print str1
        if str2: print str2

For the third, well... Use a different name...

Solution 7 - Python

This cannot work. No matter how many arguments you have, the name m will be overriden with the second m method.

class C:
    def m(self):
        print('m first')
    def m(self, x):
        print(f'm second {x}')
 
        
ci=C();
#ci.m() # will not work TypeError: m() missing 1 required positional argument: 'x'
ci.m(1) # works

The output will simple be:

m second 1

Solution 8 - Python

You probably want a pattern similar to the following: Note that adding '_' to the beginning of a method name is convention for marking a private method.

class MyClass:
    """"""

    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
    def my_method(self,parameter_A_that_Must_Be_String, param2=None):
        if type(param2) == str:
            return self._my_method_extra_string_version(parameter_A_that_Must_Be_String, param2)
        elif type(param2) == int:
            return self._my_method_extra_int_version(parameter_A_that_Must_Be_String, param2)
        else:
            pass # use the default behavior in this function
        print parameter_A_that_Must_Be_String

    def _my_method_extra_string_version(self,parameter_A_that_Must_Be_String, parameter_B_that_Must_Be_String):
        print parameter_A_that_Must_Be_String
        print parameter_B_that_Must_Be_String

    def _my_method_extra_int_version(self,parameter_A_that_Must_Be_String, parameter_A_that_Must_Be_Int):
        print parameter_A_that_Must_Be_String * parameter_A_that_Must_Be_Int

Solution 9 - Python

class MyClass:
    def __init__(this, foo_str, bar_int):
        this.__foo = foo_str
        this.__bar = bar_int

    def foo(this, new=None):
        if new != None:
            try:
                this.__foo = str(new)
            except ValueError:
                print("Illegal value. foo unchanged.")

        return this.__foo

    def bar(this, new=None):
        if new != None:
            try:
                this.__bar = int(new)
            except ValueError:
                print("Illegal value. bar unchanged.")

        return this.__bar

obj = MyClass("test", 42)
print(obj.foo(), obj.bar())

print(obj.foo("tset"), obj.bar(24))

print(obj.foo(42), obj.bar("test"))

Output:
    test 42
    tset 24
    Illegal value. bar unchanged.
    42 24

Solution 10 - Python

I think one very simple example is missing from all the answers, and that is: what to do when the only difference between variations on the method is the number of arguments. The answer still is to use a method with variable number of arguments.

Say, you start with a method that requires use of two arguments

def method(int_a, str_b):
    print("Got arguments: '{0}' and '{1}'".format(int_a, str_b)

then you need to add a variant with just the second argument (say, because the integer is redundant), the solution is very simple:

def _method_2_param(int_a, str_b):
    print("Got arguments: '{0}' and '{1}'".format(int_a, str_b))

def _method_1_param(str_b):
    print("Got argument: '{0}'".format(str_b))

def method(*args, **kwargs):
    if len(args) + len(kwargs) == 2:
        return _method_2_param(args, kwargs)
    elif len(args) + len(kwargs) == 1:
        return _method_1_param(args, kwargs)
    else:
        raise TypeError("Method requires one or two arguments")

The nice thing about this solution is that no matter if the calling code used keyword arguments or positional arguments before, it will still continue to work.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser278618View Question on Stackoverflow
Solution 1 - PythonkefeizhouView Answer on Stackoverflow
Solution 2 - Pythonuser395760View Answer on Stackoverflow
Solution 3 - PythonMark McDonaldView Answer on Stackoverflow
Solution 4 - PythonSteveMcView Answer on Stackoverflow
Solution 5 - PythongruszczyView Answer on Stackoverflow
Solution 6 - PythonmripardView Answer on Stackoverflow
Solution 7 - PythonprostiView Answer on Stackoverflow
Solution 8 - PythonbbrameView Answer on Stackoverflow
Solution 9 - PythonsudokodeView Answer on Stackoverflow
Solution 10 - PythonHubert KarioView Answer on Stackoverflow