J. Daniel
J. Daniel

Reputation: 393

Python overload __init__

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

Upvotes: 6

Views: 7065

Answers (3)

mgc
mgc

Reputation: 5443

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)

Upvotes: 4

Andrea Corbellini
Andrea Corbellini

Reputation: 17761

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.

Upvotes: 11

Padraic Cunningham
Padraic Cunningham

Reputation: 180471

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

Upvotes: 2

Related Questions