10

I'm learning Python (3.x) from a Java background.

I have a Python program where I create a personObject and add it to a list.

p = Person("John")
list.addPerson(p)

But for flexibility I also want to be able to declare it directly in the addPerson method, like so:

list.addPerson("John")

The addPerson method will be able to differentiate whether or not I'm sending a Person-object or a String.

In Java I would create two separate methods, like this:

void addPerson(Person p) {
    # Add a person to the list
}

void addPerson(String personName) {
    # Create a 'Person' object
    # Add a person to the list
}

I'm not able to find out how to do this in Python. I know of a type() function, which I could use to check whether or not the parameter is a String or an Object. However, that seems messy to me. Is there another way of doing it?

I guess the alternative workaround would be something like this (Python):

def addPerson(self, person):
    # Check if 'person' is a string
        # Create a person object

    # Check that a person is a Person instance
        # Do nothing

    # Add person to list

But it seems messy compared to the overloading solution in Java.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Einar
  • 1,001
  • 3
  • 11
  • 28
  • 7
    You may be interested in [Five-minute multimethods in Python](http://www.artima.com/weblogs/viewpost.jsp?thread=101605), written by the Benevolent Dictator himself. – Kevin Mar 13 '14 at 11:37
  • @MartijnPieters: I don't think, this is a duplicate. I think he is asking for the same methods only not the separate class to implement polymorphism. Actually, even I was looking for the same. In one class can I use same named methods twice, just the datatype of the parameter is different, but the count same?. – Laxmikant Ratnaparkhi Mar 13 '14 at 11:42
  • @LaxmikantGurnalkar: Indeed, I agree that that target is not a great dupe target. – Martijn Pieters Mar 13 '14 at 11:43
  • 4
    Better dupes: [Python function overloading](http://stackoverflow.com/q/6434482) and [Function overloading in Python: Missing](http://stackoverflow.com/q/733264) – Martijn Pieters Mar 13 '14 at 11:45
  • in Java, you have something like this: `addPerson(Person p)`. In python, like this: `addPerson(p)`. Note that in python, we haven't even told it the type, so if it could check, it doesn't know what to check against. Guido's `multimethod` supplies a way to both supply the expected types, and then emulate the type-based dispatch of C#/Java/C++.... – Corley Brigman Mar 13 '14 at 12:31
  • I would simply use an if statement with `isinstance(p, Person)` to decide whether I had been given a Person instance, and if not then create it first. As explicit as it gets, in my opinion. – RemcoGerlich Mar 13 '14 at 13:34

2 Answers2

8

Using the reference pointed to by Kevin you can do something like:

from multimethod import multimethod

class Person(object):
    def __init__(self, myname):
        self.name = myname

    def __str__(self):
        return self.name

    def __repr__(self):
        return self.__str__()


@multimethod(list, object)
def addPerson(l, p):
    l = l +[p]
    return l

@multimethod(list, str)
def addPerson(l, name):
    p = Person(name)
    l = l +[p]
    return l


alist = []
alist = addPerson(alist, Person("foo"))
alist = addPerson(alist, "bar")
print(alist)

The result will be:

$ python test.py
[foo, bar]

(You need to install multimethod first.)

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Lorenzo Baracchi
  • 1,808
  • 1
  • 13
  • 18
1

One common way I've used to implement this particular pattern is like this:

def first(self, person):
    try:
        person = Person(person)
    except ConstructionError as e:
        pass
    # Do your thing

I don't know if that will work for you or not. You want to catch whatever error Person would generate here if you call it with an existing Person. It's probably even better if that's something like person_factory, as it can just return the existing object if it's already a person instead of throwing an exception.

Multimethods are just different in Python; Python lets you implement almost any kind of semantic interface you want, at the cost of extra work under the hood somewhere, and you can usually move around where this 'somewhere' is to best hide it from you.

Also... there are advantages to this... things that you see sometimes in C++, C#, and Java like this:

 int sum(int a, int b) {
     return (a+b)
 }

 float sum (float a, float b) {
     return (a+b)
 }
 etc.

It can be replaced in Python with just

 def sum(a,b):
      return a+b

(though C++ and C# have auto now that can handle things like this too, as far as I know).

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Corley Brigman
  • 11,633
  • 5
  • 33
  • 40
  • Is this considered to be a "pythonic" way of solving it? I mean, if I want to do the same thing with Person's constructor/__init__ I might get some nasty bugs. So it seems a bit messy. Also, it's practically a type-checking solution, like the one I mentioned in the main post. – Einar Mar 13 '14 at 12:19
  • Calling `Person`'s constructor inside its own constructor is a bit dicey in any language. Any solution is going to be a 'type-checking' solution _somewhere_ - in compiled languages, the type-checking is automatic, and in the compiler; in Python, you have to do it yourself, because the type isn't even known until you actually call the function. – Corley Brigman Mar 13 '14 at 12:28
  • Ohh, sorry. I meant, if I wanted there to be multiple versions of the constructor as well. Not as in, calling the constructor recursively. If person is not a string, but the program runs without errors in person = Person(person), that would leave an opening to bugs in the future. As far as I can tell at least. But this is maybe the downside of loosely typed languages and not your solution? – Einar Mar 13 '14 at 12:45
  • It is a downside and upside of loosely typed languages... if, later on, you decide to implement a constructor for `Person` that initializes from a `dict`, in this case, nothing changes - it 'just works', because python doesn't care what `p` is - only the `Person` constructor does, and it doesn't have to decide if it likes the input until it's actually in the constructor. Your code just says 'I need a Person, so if it isn't one, make one`, and the expectation is that `Person(p)` will do that, or throw an error. – Corley Brigman Mar 13 '14 at 13:17
  • in Java, you'd have to add another overloaded signature to handle it. the downside is that the `Person` constructor could maybe construct something when you don't want it to, but I think it's easier to special-case out the exceptions than special-case in the whitelist. – Corley Brigman Mar 13 '14 at 13:19