Implementing Function Overloading in Python
arpitbhayani.me
arpitbhayani.me
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)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.
You could define it as a typing.Protocol too.
My point was just that, even if for some reason you prefer function overloading, the restrictions needed to fit within the constraints of functool.singledispatch arguably lead to cleaner code than the complicated mess in the OP article.
This would look messy if you implemented as just a single function with variable no of kwargs that you sniffed in the body, and I don't think applying function overloading alone as OP has done solves the underlying problem.
Indeed. Less is more sometimes.
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.
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.<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.
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.
This post unfortunately reads like one of those Java programmer discovers Python (except not really) posts, but for C++. In general if you find yourself inventing a new paradigm for a 30-year old language you just started working with, it's reason enough to doubt you're doing something suboptimal. However, in this case the author is actually very familiar with Python, so I'm kinda confused.
Guido's post on multimethods: https://www.artima.com/weblogs/viewpost.jsp?thread=101605
Clojure style multimethods: https://adambard.com/blog/implementing-multimethods-in-pytho...
Some sample code (I modified it to be commutative on the argument types):
https://gist.github.com/arunbear/caef7546d297c01fddbd3f67117...
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.
def area(shape):
return shape.area()
and implement it appropriately in your type.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.
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.
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)Alternatively, you could also use the abstract base classes in the numbers module - 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. :)
nzxt^2: predicate based dispatch