6

How do I overload __init__() in Python? I'm used to C/C++ where the compiler can see the difference in data type, but since there are no data types in Python, how can I make sure the third method gets called instead of the second when I give a string as parameter instead of an int?

class Handle:
 def __init__(self):
 self.pid = -1
 def __init__(self, pid):
 self.pid = pid
 def __init__(self, procname):
 print(procname)
 self.pid = -1 # find pid by name
Mike Müller
86.1k21 gold badges174 silver badges165 bronze badges
asked Dec 27, 2015 at 18:54
2
  • What you are doing is called "overloading", not "overriding", and (as the answers given indicate), it's not possible in Python. If you want different behavior based on argument types, you need to write a single method definition that checks the types itself explicitly. Commented Dec 27, 2015 at 19:02
  • @BrenBarn Thanks, you are right I used the wrong term. Commented Dec 27, 2015 at 19:06

3 Answers 3

11

In C++ you can define multiple functions/methods with the same name and different signatures. In Python, everytime you define a new function with the same name of a previously defined function, you are replacing it.

To achieve what you want, you have to use optional arguments and/or explicit checks.

Here are a few possible solutions:

class Handle:
 def __init__(self, pid=-1):
 if isinstance(pid, str):
 self.procname = pid
 self.pid = -1
 else:
 self.procname = None
 self.pid = pid

class Handle:
 # Both pid and procname may be specified at the same time.
 def __init__(self, pid=-1, procname=None):
 self.procname = procname
 self.pid = pid

class Handle:
 # Either pid or procname, not both, may be specified.
 def __init__(self, pid=-1, procname=None):
 if pid >= 0 and procname is not None:
 raise ValueError('you can specify either pid or procname, not both')
 self.procname = procname
 self.pid = pid

class Handle:
 def __init__(self, pid=-1):
 self.pid = pid
 # A separate constructor, with a different name,
 # because "explicit is better than implicit" and
 # "sparse is better than dense" (cit. The Zen of
 # Python).
 # In my opinion, this is the most Pythonic solution.
 @classmethod
 def from_process_name(cls, procname):
 pid = get_pid(procname)
 return cls(pid)

By the way, I would not recommend using -1 for "not specified". I'd rather use None.

answered Dec 27, 2015 at 19:03
Sign up to request clarification or add additional context in comments.

6 Comments

Pet peeve, which you thankfully avoided: Using if x when you really mean if x is not None. The latter is more verbose but often more correct, since '', 0, empty containers, and various other things are falsey.
The second last block would crash in multiple cases, comparing an int to any other type would cause a TypeError in python3 and in python2 some non int values would be considered > than 0.
@PadraicCunningham: welcome to the world of duck typing :)
I think a user getting a TypeError passing a value to the constructor might not be very obvious.
Well, if you pass an argument of the wrong type... what exception would you expect?
|
4

Couldn't the PEP 443 : Single-dispatch generic functions be useful in your case ?

With something like (very basic example) :

from functools import singledispatch
class Handle:
 def __init__(self, arg=None):
 self.pid = init_from_arg(arg)
@singledispatch
def init_from_arg(arg):
 # If None or neither an integer nor a string, return None
 return None
# If the argument is an integer (ie the pid)
@init_from_arg.register(int)
def _(arg):
 return arg
# If the argument provided is a string (ie the procname)
@init_from_arg.register(str)
def _(arg):
 return get_pid_from_procname(arg)

Edit : Following the advice in comments, these functions could probably be made staticmethods, although I had trouble with them so I reverted to the code of my original answer (see edit history if needed)

answered Dec 27, 2015 at 20:55

1 Comment

You can make these functions static methods. Looks a bit cleaner.
2

Use isinstance to check the type:

class Handle:
 def __init__(self, arg):
 if isinstance(arg, str):
 self.pid = arg
 else:
 self.pid = -1

If you want to do one action or another you might be better setting the arg to None and using it based on whether it is set or not:

class Handle:
 def __init__(self, by_name=None):
 self.by_name = by_name

Then just check later if it is None:

 if self.by_name is not None:
 # find by name
 else:
 # by pid

Of if you want the user to have a choice to pass either a pid or name:

class Handle:
 def __init__(self, by_name=None,by_pid=None):
 self.by_name = by_name
 self.by_pid = by_pid

The simplest solution would be to take a single arg and move the logic wherever you call it, just because something is not None does not make it an int or string so you should also check for that or your program will error:

class Handle:
 def __init__(self, proc):
 self.proc = proc
 if not isinstance(self.proc, (int,str)):
 raise ValueError("Must be process name or pid.")
 def find_proc(self):
 if isinstance(self.proc, str):
 # find by name
 else:
 # by pid
answered Dec 27, 2015 at 18:59

2 Comments

We could maybe improve the author's code a little further with a default value for arg. +1
@timgeb, maybe setting to None might be an idea alright

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.