7 ms·
Implementing Function Overloading in Python
- sleavey 7y agoIsn'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 7y agoFunctions.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 7y agoThis 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.
- Waterluvian 7y agoI 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.
- iovrthoughtthis 7y agoYou might enjoy ruby as well then!
- lordgrenville 7y agoYes, 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.
- lervag 7y agoDiscussed 1 month ago: https://news.ycombinator.com/item?id=22340720 https://news.ycombinator.com/item?id=22340720
- deleted 7y ago[deleted]
- BiteCode_dev 7y agoRemember 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 7y agoThe 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 7y ago> '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.
- 7y ago
- arunix 7y agoThis 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-python/ https://adambard.com/blog/implementing-multimethods-in-pytho...
- throwaway_pdp09 7y agoI 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 7y agoIt 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/caef7546d297c01fddbd3f671179bb70 https://gist.github.com/arunbear/caef7546d297c01fddbd3f67117...
- anentropic 7y agoThis 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 7y agoAgain 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.
- anentropic 7y agoYes I agree, if I was writing this for my own project I'm pretty sure I would do what you have done and use classes with an `area` method. (FWIW I think the dataclass approach is nicer for this example than namespacing separate `area` functions using modules) 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.
- trymas 7y agohttps://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.
- goodside 7y agoThis 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 7y agoIf you want to "overload" a function. Do def area(shape): return shape.area() and implement it appropriately in your type.
- mrkeen 7y agoIf 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 7y agoYou 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 7y agoUsing fancy features does not make you a better programmer. It is the other way around.
- mrkeen 7y agoBeing a better programmer makes you use fancy features?
- modo_ 7y agoCharitably 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)
- jstrong 7y ago"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
- whalesalad 7y agoSingledispatch 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.
- detaro 7y agoduplicate, please don't repost so quickly: https://news.ycombinator.com/item?id=22340720 https://news.ycombinator.com/item?id=22340720
- jl2718 7y agoI would have expected polymorphism with type hints. Oh well.
- agumonkey 7y agonext: type based dispatch nzxt^2: predicate based dispatch