
Implementing Function Overloading in Python - arpitbbhayani
https://arpitbhayani.me/blogs/function-overloading
======
anentropic
This is pretty horrible.

And you could achieve the same thing with built-in functools.singledispatch by
designing your API better

e.g. the example given is:

    
    
        int area(int length, int breadth) {
          return length * breadth;
        }
    
        float area(int radius) {
          return 3.14 * radius * radius;
        }
    

What if you need to calculate the area of another shape that uses a single
integer value as the parameter?

With singledispatch and some types:

    
    
        import math
        from functools import singledispatch
        from typing import NamedTuple
    
        class Rectangle(NamedTuple):
            length: int
            breadth: int
    
        class Circle(NamedTuple):
            radius: int
    
        @singledispatch
        def area(val) -> None:
            raise TypeError(val)
    
        @area.register
        def _(rectangle: Rectangle) -> int:
            return rectangle.length * rectangle.breadth
        
        @area.register
        def _(circle: Circle) -> float:
            return math.pi * math.pow(circle.radius, 2)

~~~
BiteCode_dev
Again this is over-engineering. Put functions in modules to namespace them and
be done with it.

If you really want types, use classes. It removes the need for single
dispatch, creates a self documenting namespace and saves you an import when
you want to do the calculation.

    
    
        import math
        from dataclasses import dataclass
    
        @dataclass
        class Rectangle:
            length: int
            breadth: int
            def area(self) -> int:
                return self.length * self.breadth
    
        @dataclass
        class Circle:
            radius: int
            def area(self) -> float:
                return math.pi * self.radius ** 2
    
    

@dataclass is of course not mandatory, and just make the code shorter.

We are talking about very simple things here. No need to add complexity to
them.

~~~
jakearmitage
Didn't know about @dataclass until now. Thank you.

~~~
oefrha
See also attrs [http://www.attrs.org/](http://www.attrs.org/) which is one of
the main inspirations of py37 dataclass and is more feature-rich.

------
Waterluvian
I love Python as a "hack it into a Frankenstein monster for fun" language
because of how much power you have to get under the hood. For that reason I
love articles like this.

But I'm not sure you'd ever want to implement overloading for practical uses.
Just utilize optional arguments and some branching logic within the function.
But even then, rethink how much you're trying to pack into a function.

~~~
lordgrenville
Yes, my reaction to this was "this is horrifying Python, _never_ do this in
real life!" But also fascination at how malleable and subvertible it is once
you go deep enough into the internals.

------
BiteCode_dev
Remember that duck typing + default values + args + kwargs is usually more
idiomatic than function overloading in python.

Don't try to code in python like you code in another language.

Also, think about what your API is conveying. Overloading may make it less
clear.

The snippet from this article is a good example of when not to do it:

    
    
        def area (l, b): 
          return length * breadth;
    
    
        def area(r):
          return 3.14 * radius * radius
    

Here, you have 2 functions that do 2 fundamentally different things. One is
calculating an area for a circle. The other one is for a rectangle. When you
have 2 different signatures, it's often a sign the process is very different,
and you should signal that in your API.

If you scan code using those, your brain cannot quickly parse it. It needs to
process "area(), ok but of what, well, it has only x param, so I guess this is
for z". The overloading gimmick has little value for the reader, but a real
cost (not to mention in python it actually slows the run time)

The better solution is simply to make it explicitly 2 functions with good
names:

    
    
        def rectangle_area (l, b): 
          return length * breadth;
    
    

Or, most likely, put them in separated namespaces (using a module or a class),
which does the same.

    
    
        import circle
    
        circle.area(r)       
    

It's very easy to abuse overloading because it tickles our need for
refactoring. But it's a tool to solve a problem, not an end by itself.

~~~
throwaway_pdp09
The first part seems the wrong way to do it (treating it as an example). You
would presumably overload area() on the object type, not on individual
parameters representing the object's geometry. 'circle.area(r)' is even
weirder - extracting radius just doesn't make sense here.

<SomeGeometricObject>.area() is right AFAICS.

Which I think is what you're getting at anyway.

But IMO overloading used right is cleaner than not having overloading.

~~~
BiteCode_dev
> 'circle.area(r)' is even weirder

It's just putting the original area() function in a module, not making it a
method of a class. It's the simplest solution you can have, ever.

Adding an object is already more complex, and complexity needs to be
justified. Just KISS if you can.

------
arunix
This seems like a lot of work for a result that isn't even very self
documenting compared to some earlier efforts e.g.

Guido's post on multimethods:
[https://www.artima.com/weblogs/viewpost.jsp?thread=101605](https://www.artima.com/weblogs/viewpost.jsp?thread=101605)

Clojure style multimethods: [https://adambard.com/blog/implementing-
multimethods-in-pytho...](https://adambard.com/blog/implementing-multimethods-
in-python/)

~~~
throwaway_pdp09
I remember reading the multimethods post and thinking it was wrong as it does
not dispatch on all arguments, as dylan would (and I believe closure would
too) - correct me if I'm wrong though.

~~~
arunix
It does dispatch on the types of all arguments, ... that's the point of it.

Some sample code (I modified it to be commutative on the argument types):

[https://gist.github.com/arunbear/caef7546d297c01fddbd3f67117...](https://gist.github.com/arunbear/caef7546d297c01fddbd3f671179bb70)

------
goodside
This borders on spam. This is the third time you’ve submitted this post in the
past two months and the feedback is no more positive this time.

Yes, you can do just about anything in Python. But single dispatch is usually
a bad idea, and implementing it yourself instead of using the Python stdlib is
always a bad idea.

There’s nothing wrong with showing off programming curiosities, but this isn’t
presented in that context. The more often you post it, especially without
heeding or engaging with feedback, the more annoying it becomes.

------
enricozb
If you want to "overload" a function. Do

    
    
        def area(shape):
          return shape.area()
    

and implement it appropriately in your type.

------
trymas
[https://pypi.org/project/multidispatch/](https://pypi.org/project/multidispatch/)

IMHO, multiple dispatch is unpythonic and you should use it only if you really
need it. Also OP's implementation is missing typing information and
differentiates functions only by length of arguments. Though probably that's
the reason I do not like this idea in python because it's not a statically
typed language.

------
lervag
Discussed 1 month ago:
[https://news.ycombinator.com/item?id=22340720](https://news.ycombinator.com/item?id=22340720)

------
whalesalad
Singledispatch from functools will often work in a lot of situations where you
might want to do something like this:
[https://docs.python.org/3/library/functools.html](https://docs.python.org/3/library/functools.html)

This is a cool post from the author, though. I interpret this as more of an
exploration of Python under the hood versus being a recommended production
solution.

Python certainly could use a facility to do that based on an arbitrary
predicate fn versus type comparison, though.

Python is well suited for this.. maybe it is time to throw a PEP in the ring.

------
jstrong
"Yeah, but your scientists were so preoccupied with whether or not they could,
they didn’t stop to think if they should." -Dr. Ian Malcolm

------
sleavey
Isn't this behavior in Python already via the functools module? Can't look it
up right now but I'm pretty sure it's there.

~~~
joshuamorton
Functions.singledispatch allows overloading based on the type of the first
argument, but doesn't allow complex overloads, like arg-count based or multi-
type based.

~~~
emidln
This is not hard to write if you care about it (dispatching on data in
addition to dispatching on type). Clojure-style multimethods with python-style
type dispatch or data result dispatch can be had for under 150 lines. It's not
particularly useful unless you are porting code or otherwise writing very
unpythonic code.

------
mrkeen
If you allow both

    
    
      int area(int length, int breadth)
    

and

    
    
      float area(int radius)
    

to exist, then in the future you won't be able ask what the type of 'area' is.
(If that's your kind of thing)

~~~
zomglings
You will, the type could be Union[int, float].

Alternatively, you could also use the abstract base classes in the numbers
module -
[https://docs.python.org/3/library/numbers.html](https://docs.python.org/3/library/numbers.html)

numbers.Number may be a good choice.

Still, you are right, overloading like this may be okay as a thought exercise,
but would raise some serious questions during a code review. :)

------
klhugo
Using fancy features does not make you a better programmer. It is the other
way around.

~~~
mrkeen
Being a better programmer makes you use fancy features?

~~~
modo_
Charitably interpreted: A better programmer knows using a fancy feature _can_
result in better code, but, also knows that fancy features usually aren't
required to write good code (and that fancy features can turn good code into
bad code when reached for too quickly)

------
detaro
duplicate, please don't repost so quickly:
[https://news.ycombinator.com/item?id=22340720](https://news.ycombinator.com/item?id=22340720)

------
jl2718
I would have expected polymorphism with type hints. Oh well.

------
agumonkey
next: type based dispatch

nzxt^2: predicate based dispatch

