How to perform different actions depending on parameter type

2.1k Views Asked by At

In languages that use static binding like java you can define multiple functions all having the same name but different parameters. Learning Python, until now I considered the lack of this mainly as "safety issue" (like bool_parameter="False" might be interpreted as True because of the quotes). I thought I would simply need to be more careful.

Now I found a situation, where the lack of static binding is simply inconvenient. Please consider this tupel:

var = ((1, "foo"), (2, "bar"), (3, "potato"))

To remove an item from var with static binding one could do something like this(pseudocode:

def del_item(int i):
    # search item with (x == i, *)
    # remove this item

def del_item(String s):
    # search item with (*, x == s)
    # remove this item

I find this very convenient, because no conditions are needed to select the right action to perform. Furthermore this code makes overloading easier, as one can decide to just overload one of the functions or both.

Trying to deal with a situation like this in Python, I only find inconvenient solutions like some if-clauses that check for the type.

Is there a better way?

4

There are 4 best solutions below

1
On BEST ANSWER

Python doesn't have overloading of methods so you're going to have to check the type of the argument sorry.

def del_item(item):
    if type(item) is int:
        # search item with (x == item, *)
        # remove this item
    elif type(item) is str:
        # search item with (*, x == s)
        # remove this item
    else:
        # Maybe raise an exception?
4
On

Your problem could be tackled by using generic methods/functions. These don't come built-in with python, but can be roped in by either a 3rd-party library, or you write one yourself.

I have been working happily with PEAK rules a few years ago, but while it should still work, it seems to have fallen out of favour a bit.

The new PEP 443 (single argument dispatch) is accompanied by external implementation, singledispatch. https://pypi.python.org/pypi/singledispatch/3.4.0.3

With that, your problem could be solved like this:

 from functools import partial
 from singledispatch import singledispatch


 var = ((1, "foo"), (2, "bar"), (3, "potato"))


 @singledispatch
 def del_predicate(value):
     pass


 @del_predicate.register(int)
 def _(v, candidate):
     return v == candidate[0]

 @del_predicate.register(str)
 def _(v, candidate):
     return v == candidate[1]


 def neg(f):
     def _f(*args):
         return not f(*args)
     return _f

 print filter(neg(partial(del_predicate, "foo")), var)
 print filter(neg(partial(del_predicate, 2)), var)
0
On

The particular case you give seems like one where overloading is not needed anyway.

def del_item(id):
    return tuple(item for item in var if not id in item)

Another option is to use optional keyword arguments

def del_item(string_id=None, number_id=None):
    if string_id is not None:
        return tuple(item for item in var if not item[1] == string_id)
    return tuple(item for item in var if not item[0] == number_id)

There are lots of previous questions on python overloading, and this is one answer that might help understand why it's not seen as a problem not to have it.

0
On

check out this question: Differences between isinstance() and type() in python

If you end up doing the if type approach suggested, you may want to consider duck typing or isinstance alternatives