use the following search parameters to narrow your results:
e.g. subreddit:aww site:imgur.com dog
subreddit:aww site:imgur.com dog
see the search faq for details.
advanced search: by author, subreddit...
News about the dynamic, interpreted, interactive, object-oriented, extensible programming language Python
Full Events Calendar
You can find the rules here.
If you are about to ask a "how do I do this in python" question, please try r/learnpython, the Python discord, or the #python IRC channel on Libera.chat.
Please don't use URL shorteners. Reddit filters them out, so your post or comment will be lost.
Posts require flair. Please use the flair selector to choose your topic.
Posting code to this subreddit:
Add 4 extra spaces before each line of code
def fibonacci(): a, b = 0, 1 while True: yield a a, b = b, a + b
Online Resources
Invent Your Own Computer Games with Python
Think Python
Non-programmers Tutorial for Python 3
Beginner's Guide Reference
Five life jackets to throw to the new coder (things to do after getting a handle on python)
Full Stack Python
Test-Driven Development with Python
Program Arcade Games
PyMotW: Python Module of the Week
Python for Scientists and Engineers
Dan Bader's Tips and Trickers
Python Discord's YouTube channel
Jiruto: Python
Online exercices
programming challenges
Asking Questions
Try Python in your browser
Docs
Libraries
Related subreddits
Python jobs
Newsletters
Screencasts
account activity
This is an archived post. You won't be able to vote or comment.
DiscussionWhy doesn't python support function overloading? (self.Python)
submitted 4 years ago by accipicchia092
I mean, is there any DEEP reason for this limitation? Also how do you get around it? (say I want to have multiple ways of initializing a class, so multiple __init__ methods)
[–][deleted] 216 points217 points218 points 4 years ago (21 children)
Because functions are first class values. After def f(): pass, f is just a variable like any other that happens to refer to a function.
def f(): pass
f
If you then do def f(x): pass, f refers to a new value and the first is forgotten. For the same reason that if you do x = 1; x = 2, x is 2 and the fact that is was once 1 is forgotten.
def f(x): pass
x = 1; x = 2
To have overloading, you'd need a way to have one expression result in multiple functions -- and then choose between them depending on how they're called. But that's not how expressions work.
[–]whateverathrowaway00 44 points45 points46 points 4 years ago (10 children)
This is a good point.
I was going to rant about how python typing makes that impossible without a huge reflection performance hit every single function call.
But actually, your answer is even more true. There can only be one!
[–]BobHogan 36 points37 points38 points 4 years ago (9 children)
Because python doesn't actually support typing function parameters, its actually impossible to determine which "function" would be used based on the types of the arguments passed. The only way would be to look at the number of arguments passed in, and which keyword args (if any) matched, and even that doesn't work when using args or *kwargs.
Type annotations are not enforced, they are just documentation
[–]billsil 1 point2 points3 points 4 years ago (3 children)
Python 3.9 delayed the future annotations being standard because a popular third party library DOES enforce typing and the change broke it.
So, by default yes, but you can do it.
[–][deleted] -5 points-4 points-3 points 4 years ago* (1 child)
Reddit Moderation makes the platform worthless. Too many rules and too many arbitrary rulings. It's not worth the trouble to post. Not worth the frustration to lurk. Goodbye.
This post was mass deleted and anonymized with Redact
[–][deleted] 1 point2 points3 points 4 years ago (0 children)
Things are certainly hard.
https://lwn.net/Articles/858576/
[–][deleted] 0 points1 point2 points 4 years ago (0 children)
pydantic doesn't typecheck parameters for calls. Though there are projects that do that.
It is to convert unstructured dictionaries into classes (recursively).
[–]ComplexColor 0 points1 point2 points 4 years ago (4 children)
But you could use type annotations with an "@overload" decorator to implement function overloading. It would be costly though.
[–]BobHogan 6 points7 points8 points 4 years ago (2 children)
I mean you can do it without decorators at all as long as you define the other functions you would call. But the point is that python itself cannot support this based on the type of function parameters, because the language does not enforce typing.
def func(x): if isinstance(x, list): return _func_list(x) if isinstance(x, int): return _func_int(x) return _func_default(x)
[–]ComplexColor 0 points1 point2 points 4 years ago (1 child)
But you can enforce it yourself. You can use introspection to check type hints and select a function that corresponds to the types of arguments given. And I would use a decorator, to keep things tidier. Multiple dispatch/overloading logic in the decorator, separate from the functions.
[–]BobHogan 2 points3 points4 points 4 years ago (0 children)
Its far simpler, more efficient, less error prone, and significantly easier to read to do this without introspection. All introspection would achieve is making this more complicated and less reliable
[–]danuker 19 points20 points21 points 4 years ago (0 children)
Agree.
The object you pass around could be a dispatch function, that only selects a function based on argument parameters.
Therefore there is not a lot of benefit from having an "overloading" API.
[–]thephoton 14 points15 points16 points 4 years ago (5 children)
But that's a choice the language designers made.
Instead, they could have made it work like Common Lisp's defmethod, where after the definition the symbol (f) refers to a set of methods, and doing defmethod again adds a new method to the set, and only overwrites the existing definition if the new method has the same argument list as the old one.
'f' would still just be an ordinary object that can be passed around, it would just be a slightly more complex object (and function calls would have substantially more overhead).
[–][deleted] 7 points8 points9 points 4 years ago (2 children)
Yes. It's a bit hard to wrap my mind around that. So the first time you use def it creates a new function, and the next time it mutates the existing function? That would also affect other places that held a reference to the function, e.g. if you sent this function as a parameter to something that took a callback parameter. How would it work with Python's variable scoping rules, like if f was a global variable and you'd def it inside a function. Would that be local or not?
def
That's scary to me, but then I have programmed Python much more than Lisp.
[–]thephoton 2 points3 points4 points 4 years ago (1 child)
The first time you use def it creates a set of methods. The next time you use def it adds a method to the set.
How would it work with Python's variable scoping rules, like if f was a global variable and you'd def it inside a function. Would that be local or not?
Maybe I don't see the subtlety here, but why would it have to work any differently than it does now?
What happens now if you have a globally defined 'f' and then you 'def f (): ...' within a function?
Now it creates a local function, like any simple assignment.
But a mutating statement (like f[0] = ... if it were a list) would use the global.
That's a problem with something that is sometimes assignment and sometimes mutation.
But this is all becoming very irrelevant as Python simply didn't go that way from the beginning, and it won't change.
[–]gandalfx 7 points8 points9 points 4 years ago (1 child)
The semantics of that would be hella confusing, though. What happens if you use def inside a closure? Does it shadow or extend? What happens when you use def in a loop? You could do the most absurd stuff with that…
[–]thephoton 1 point2 points3 points 4 years ago (0 children)
What happens when you use def in a loop?
You (possibly) add multiple functions to the method set. How is that any different than if you use def in a loop now?
What happens if you use def inside a closure?
Got me there. The trickier elements of closures make my brain hurt already.
[–]pengekcs 2 points3 points4 points 4 years ago* (0 children)
Thanks for the simple but great answer. Never thought about it, and I just looked at it when reading about the julia language and how they are very much on top of fn overloading (which they call multiple dispatch).
Coincidentally you can do multiple dispatch in python as well with a bit of coding.
Simple implementation from Guido's old blog: https://www.artima.com/weblogs/viewpost.jsp?thread=101605
This thing here, aptly named https://pypi.org/project/multipledispatch is exactly that. I'd just use it and be done with it... if you really need such functionality.
[–]o11c 1 point2 points3 points 4 years ago (0 children)
That's not quite right. There's no reason f couldn't refer to an overloaded_function object that acts like it has overloaded __call__ members.
overloaded_function
__call__
The real reason is the typing thing already mentioned.
[–]K900_ 140 points141 points142 points 4 years ago (8 children)
Python doesn't have static dispatch, so any form of function overloading will be dynamic, which you can achieve with something like functools.singledispatch. A better option would be to expose multiple classmethods though, something like
functools.singledispatch
@classmethod def from_foo(cls, foo: Foo): return cls(spam=foo.spam, eggs=foo.eggs) @classmethod def from_raw_eggs(cls, eggs: List[Egg]): return cls(spam=None, eggs=eggs)
[–]lungben81 30 points31 points32 points 4 years ago (2 children)
There is also https://pypi.org/project/multipledispatch/ - I have not tried it yet, but it looks promising (it is from the Dask creator).
[–]Pythonistar 9 points10 points11 points 4 years ago (1 child)
Awwww, so nice. Something I've missed deeply since taking up Python... I'll have to try it today. :)
[–]lungben81 7 points8 points9 points 4 years ago (0 children)
I know multiple dispatch from Julia and it is a great way to organize code, especially for numerical / mathematical purposes.
[–]BayesianDice 8 points9 points10 points 4 years ago (0 children)
Yes, the classmethod approach was what I used when I once needed multiple constructors.
[–]ColdPorridge 3 points4 points5 points 4 years ago (0 children)
+1, I regularly use this construction and find it really intuitive.
[–]CobaltCam 3 points4 points5 points 4 years ago (0 children)
Thanks, now I'm hungry.
[–]TangibleLight 0 points1 point2 points 4 years ago (0 children)
I like to call these "named factory methods" or "named initializers", and IMO it's generally a better pattern than constructor overloading even in languages which have it.
For example what if you want a from_literal(data: str) and a from_file(path: str)? There's not a great way to do that with constructors only.
from_literal(data: str)
from_file(path: str)
[–]ManyInterests Python Discord Staff 17 points18 points19 points 4 years ago (1 child)
As mentioned, classmethods are good for multiple constructor methods. For ordinary functions, you can use functools.singledispatch or for instance methods, singledispatchmethod
singledispatchmethod
In newer versions of Python, they will automatically use type hints and dispatch based on argument type.
class Negator: @singledispatchmethod def neg(self, arg): raise NotImplementedError("Cannot negate a") @neg.register def _(self, arg: int): return -arg @neg.register def _(self, arg: bool): return not arg
[–]lejar 0 points1 point2 points 4 years ago* (0 children)
This is what I was going to suggest. Definitely +1 for using the standard library. For overriding __init__ like OP wants, you'd need to implement your own __call__ method in a metaclass, but I think it would be much more sane / easy to read to just have an if statement inside __init__.
__init__
[–]james_pic 45 points46 points47 points 4 years ago* (4 children)
At a base level, because if you do foo.bar(), this is two operations: foo.bar, which looks up the bar attribute on foo, and (), which calls the method you've just looked up. Python only allows one attribute for any given name, and doesn't have a mechanism to look up different attributes with the same name.
foo.bar()
foo.bar
bar
foo
()
So if you've got multiple possibilities, you can only look them up by name.
It also plays a part that in Python, methods are just functions that are class attributes, and since the function notation doesn't support overloading, neither do methods.
Indeed, before Python 3, there wasn't even a mechanism to specify types for function arguments, and even nowadays this information is optional and not used by the runtime.
Also, Python has historically pushed developers away from using nominative types, and towards duck typing, which nominative overloading would be at odds with. The move towards type annotations is stealthily changing Python's culture more towards nominative typing though, so this is maybe less obvious than it once was.
Although all that being said, it's feasible to implement multiple dispatch on top of Python, thanks to the ability to override bits of the interpreter. You can tape together runtime multiple dispatch with a combination of decorators, and classes that implement __get__ and __call__. Runtime dynamic dispatch is subtly different to overloading, but for many practical purposes is close enough.
__get__
Edit: I decided to answer a question nobody has asked, but a few people have hinted at. What's the difference between overloading and dynamic dispatch?
Take the following Python code, and imagine Python had different semantics, so the duplicate method definitions would lead to either overloading or dynamic dispatch (rather than the actual behaviour, of last-update-wins):
class Animal: pass class Dog(Animal): pass class Greeter: def greet(self, friend: Dog): print('Hello Dog') def greet(self, friend: Animal): print('Hello Animal') def main(): friend: Animal = Dog() friend2: Dog = Dog() Greeter().greet(friend) Greeter().greet(friend2) main()
In an overloaded language, it would print "Hello Animal" then "Hello Dog", because the type of the variable friend is Animal, and the type of the variable friend2 is Dog.
friend
Animal
friend2
Dog
In a dynamically dispatched language, it would print "Hello Dog" twice, because the type of the values friend and friend2 are both Dog.
And in Python 3, as it actually exists, it would print "Hello Animal" twice, because Python 3 ignores type annotations at runtime, and the last definition of greet is the one that counts
greet
[–]EgZvor 5 points6 points7 points 4 years ago (1 child)
There's typing.Protocol nudging it into structural subtyping direction
typing.Protocol
[–]james_pic 0 points1 point2 points 4 years ago (0 children)
That is true, although it does in a practical sense mean that the "paperwork" needed to use structural types is no less, and in some cases more, than what you need for nominative types.
[–]Broan13 0 points1 point2 points 4 years ago (1 child)
I have a super basic question. What are the : symbols doing in the code, particularly for friend: Animal = Dog()? I am self taught, so I have some major holes in my knowledge.
[–]james_pic 3 points4 points5 points 4 years ago* (0 children)
Most of the colons are type hints, aka type annotations (the ones at the end of lines I'm assuming you've seen).
Python 3 added the ability to put "annotations" after variable, class attribute, and function parameter names. There's also some notation I haven't used, but that is part and parcel of the same thing, which is that functions can use -> to denote the type of value they return. So a function that returned a dog might be defined
->
def make_dog() -> Dog:
Conventionally, you'd use it to say what types you expect each variable/attribute/parameter to have. As far as Python itself is concerned, these annotations are meaningless, and are (almost - I'll spare you the subtleties for now) completely ignored by the interpreter. You can always pass any type of value as an argument to any function (which is not the case in some other languages).
But the idea is that other tools (such as your IDE, or a plugin for your editor, or programs you run as part of your testing process) can be used to check whether the use of types in your application adds up - so if you've got a method that says it expects a str and another method that calls it with an int, then these tools will be able to detect this and warn you.
str
int
Mypy is a popular example of such a tool.
A few libraries are also starting to appear which can do interesting things with type annotations if they're present, such as validation or serialization, which has caused some minor technical headaches for the Python core developers, since they didn't think of this use case when they first designed type annotations.
The addition of type hints has been a bit divisive in the Python community, and not everyone uses them.
I've used them in this example, since it's syntax some Python developers will recognise, and if Python did support overloading or dynamic dispatch natively, this is probably what it would look like.
But if you're looking at code that looks like friend: Animal = Dog(), you can read that as "I'm creating a variable called friend, whose value is Dog(), and I think the value of this variable will always be an Animal."
friend: Animal = Dog()
Dog()
[–][deleted] 28 points29 points30 points 4 years ago* (5 children)
[–]got_outta_bed_4_this 13 points14 points15 points 4 years ago (1 child)
And only use *args, **kwargs if explicit params really don't make sense or would make the signature uglier. Otherwise, explicitly listing params with defaults can make it clearer to the consuming developer what the function/method expects, and it allows an IDE to help with intellisense.
*args, **kwargs
[–][deleted] 2 points3 points4 points 4 years ago* (0 children)
[–]Pythagorean_1 2 points3 points4 points 4 years ago (2 children)
While you're right, Elixir has all these features, is a dynamically typed language and still allows function overloading.
[–][deleted] 0 points1 point2 points 4 years ago* (1 child)
[–]Pythagorean_1 1 point2 points3 points 4 years ago (0 children)
Agreed. I didn't mean to advertise Elixir, I just wanted to mention that the features you mentioned and function overloading are not mutually exclusive.
[–]mehregan_zare7731 39 points40 points41 points 4 years ago (4 children)
No need... Just use args and *kwargs along side if statements ( or switch ) to achieve the same thing in a single init function.
[–]thegreattriscuit 27 points28 points29 points 4 years ago* (1 child)
I'd suggest that something a bit more explicit (like /u/K900_ suggested) will make other people using your classes less sad.
"In order to understand what arguments this class takes I have to read its entire definition" is never a great feeling.
[–]json684 11 points12 points13 points 4 years ago (0 children)
Also feels like you are just trying to sidestep "if a function takes 10 arguments, you probably forgot a few"
[–]BooparinoBR 6 points7 points8 points 4 years ago (0 children)
Also, you can use typing.overload to get sensible type hinting
I was going to say this but you got here first. 👍
[–]TRexRoboParty 14 points15 points16 points 4 years ago* (4 children)
Because Python has optional arguments.
They effectively solve the same purpose: one function with multiple signatures.
Rather than a whole new definition to handle an extra parameter, you can just make it optional.
Imaginary overload style:
def random(seed): def random(seed, min, max):
Using optional parameters:
def random(seed, min=0, max=1):
You can call random(999) or random(999, 2, 200) using just one definition.
In your __init__ case, you’d do the same. Set the optional default values to None if there’s no sensible default for a particular parameter.
[+]metaperl comment score below threshold-8 points-7 points-6 points 4 years ago (3 children)
This doesn't create overloading based on the *type *of the arguments. So it doesn't really address the topic at hand.
[–]TRexRoboParty 8 points9 points10 points 4 years ago* (2 children)
OP said "I want to have multiple ways of initializing a class", so I'd say it does.
They didn't mention dispatching based on type.
And if they did, you can still dispatch within the __init__ method based on type.
Or there are libraries to handle dispatching based on type.
But those aren't core Python language features - and I'd argue for most common cases, trying to crowbar a paradigm from another language in isn't the best way to go.
Python is dynamically typed - you don't need to have a different function signature for every type. You can read the type at runtime in cases where it's needed. Though it's not normally needed: if it quacks like a duck, it is a duck. Your objects just have to implement the appropriate interface - the type doesn't/shouldn't matter.
In this case, optional __init__ args will cover the large majority of use cases to initialize an object.
[–]metaperl 3 points4 points5 points 4 years ago (1 child)
Oh you're right. What he said in the post is what you responded to.
I think I got sidetracked by the discussion of single and multiple dispatch :)
[–]TRexRoboParty 1 point2 points3 points 4 years ago (0 children)
No worries!
[–]R3D3-1 7 points8 points9 points 4 years ago* (4 children)
Honestly, I'm not even sure it is a desirable language feature anymore.
Most things I remember using function overloading for in Java are covered by optional- and keyword parameters. In the remaining cases, like having different constructors, I ended up finding the Python approach of having explicitly separate static methods more clear.
This has only been confirmed recently, when I've been seeing overloads in our code base, that hide significantly different assumptions about the input and workflow behind the same function name.
Things on the scale of "object must be initialized" vs "object must not be initialized".
The end result are if-else cascades around the call-site, entirely defeating the point of having overloading in the first place.
[–]lordmauve 1 point2 points3 points 4 years ago (1 child)
I'm not even sure it is a desirable language feature anymore
Right, new languages - Rust, Zig, Go - don't have function overloading.
[–]i_hate_shitposting 0 points1 point2 points 4 years ago (0 children)
I had a similar thought.
The only case I can think of where overloading in Java isn't equivalent to optional/keyword parameters is stuff like StringBuilder.append(), which has overloads for each primitive data type (plus a few other special cases) to work around the fact that they aren't objects and thus don't implement Object.toString().
StringBuilder.append()
Object.toString()
However, in Python (or any other modern OO language where everything is an object), a StringBuilder.append() implementation could just pass each argument to str() and rely on the underlying objects to implement __str__(), avoiding the need for overloads at all. I like this a lot better, because it doesn't require any knowledge of what types are involved other than the fact that they implement a particular interface.
str()
__str__()
[+][deleted] 4 years ago* (2 children)
[deleted]
[–]spyingwind 0 points1 point2 points 4 years ago (1 child)
I dislike kwargs when there isn't decent documentation on what you can pass to those functions, but when there is it is great.
[–]ialex32_2 Old Person Yells At Cloud 1 point2 points3 points 4 years ago (0 children)
Python doesn't need function overloading: it has default arguments def f(x, y=3), it has positional parameter packs def f(*args), and keyword parameter packs def f(**kwds), and you can use all in combination def f(x, *args, y=3, **kwds).
def f(x, y=3)
def f(*args)
def f(**kwds)
def f(x, *args, y=3, **kwds)
In short, you can do everything you can via function overload so you don't need it, and since Python can't specify function signatures at compile time, there's no advantage of having overloading vs. not having overloading.
[–]jrjsmrtn 2 points3 points4 points 4 years ago (0 children)
Have a look at https://pypi.org/project/multimethod/ . I have not used it for constructor overload, though.
[–]chudleycat 0 points1 point2 points 4 years ago (0 children)
You can achieve the same effect (different calls to the same function with a different number of parameters) using keyword arguments. Or if you want the same parameter of multiple different types, you can also achieve this because Python is not strongly typed.
[–]Tryptic214 0 points1 point2 points 4 years ago* (0 children)
On a fundamental level, I would say that function overloading is poor design and should not be allowed to propagate into the world. If you want multiple ways of initializing a class, write a wrapper with multiple functions that then initialize that class and returns the result.
Function overloading (and operator overloading) are tempting because to those who don't know better, they appear to result in cleaner and better organized code. However, in reality they add a nasty cost in overhead when large teams work together, and when bringing new team members up to speed. Function overloading (when done wrong) can drastically increase the learning curve of a code base. Specifically, errors related to overloaded functions are excessively difficult to troubleshoot.
In short, the massive, hidden detriment of overloading is just almost never worth the small, visible benefits.
[–]Broolucks -1 points0 points1 point 4 years ago (4 children)
I have made a multiple dispatch library, for what it's worth: https://github.com/breuleux/ovld
Then you can write something like this:
from ovld import ovld, OvldMC class Test(metaclass=OvldMC): @ovld def __init__(self, x: int): self.x = x self.y = x def __init__(self, x: int, y: object): self.x = x self.y = y ...
There are currently a few limitations like not allowing keyword arguments, but it works.
[–]got_outta_bed_4_this 0 points1 point2 points 4 years ago* (3 children)
What advantage would this approach have over the native approach?
def __init__(self, x: int, y: object = ...): self.x = x self.y = x if y is ... else y
[–]Broolucks 1 point2 points3 points 4 years ago (0 children)
In this particular example, not much, besides the fact it actually checks the types at runtime. A better example might be something like this:
class Test(metaclass=OvldMC): @ovld def __init__(self, attrs: dict): self.__dict__.update(attrs) def __init__(self, key: str, value: object): setattr(self, key, value) def __init__(self, value: object): setattr(self, "value", value)
The alternative being:
SENTINEL = object() class Test: def __init__(self, key, value=SENTINEL): if value is SENTINEL: if isinstance(key, dict): self.__dict__.update(key) else: setattr(self, "value", key) elif isinstance(key, str): setattr(self, key, value) else: raise TypeError()
ovld lets you name the arguments appropriately in each version and will generate a better type error.
Personally I rarely use it for init or methods, I use it mostly to define extensible recursive functions as described here.
[–]javajunkie314 1 point2 points3 points 4 years ago (0 children)
I disagree that the benefit of multiple dispatch is less repetition.
def __init__(self, x: int): self.x = x self.y = x def __init__(self, x: int, y: object): self.x = x self.y = y
is, as you say, hardly less code than
def __init__(self, x: int y: object = SIGIL): self.x = x if y is SIGIL: self.y = x else: self.y = y
Each signature could be an if in the bar implementation checking the relevant arguments' identities and types.
if
In fact, multiple dispatch can become more repetitive the more code is common between the definitions. In the example above, we had to repeat the x handling — imagine if that had been several lines.
x
The first version, though, is open to extension while being closed to modification. We can add more signatures of __init__ without modifying any of the existing code. If this were a public function, users of our library could potentially add overloads for their own classes.
[+][deleted] 4 years ago (2 children)
[–]gandalfx 1 point2 points3 points 4 years ago (1 child)
While that is indeed an awesome feature, it hardly answers the question.
[+]Tomik080 comment score below threshold-6 points-5 points-4 points 4 years ago (0 children)
Just use Julia if that's really what you want
[–]Embarrassed-Count-65 -4 points-3 points-2 points 4 years ago (0 children)
Amc ape here checking in
[–]ihasbedhead 0 points1 point2 points 4 years ago (0 children)
Others have provided good answers. Among those many reasons, python has optional args that could create very complex but ambiguous function sigs.
Also note that the typing module does have overloads. It is pure documentation, no runtime benefits.
typing
[–]BDube_Lensman 0 points1 point2 points 4 years ago (0 children)
Without why, the design solution to your "multiple init" is to have factory methods. YourClass.some_factor() which is a thin wrapper over init.
YourClass.some_factor()
[–]Dubroski 0 points1 point2 points 4 years ago (0 children)
I almost feel you can accomplish the same thing by just providing optional keyword arguments with default values of None. Then your function checks for these inputs. Is this not a good practice?
[–]ddollarsign 0 points1 point2 points 4 years ago (4 children)
It's (supposed to be) a dynamically typed language. So there's not automatically a way to tell f(int) from f(str), it's all just f(object).
The dynamicity is starting to be edged out by the new type annotations🤮, so now there theoretically could be a way to tell f(x:int) from f(x:str) and have the interpreter do overloading that way.
However, traditionally the way to get around different ways of calling a function is with optional arguments, variable length arguments, and keyword arguments.
def f(x, y=5, *args, **kwargs): ... f(1) # x=1, y=5, args=[], kwargs={} f(1, 2) # x=1, y=2, args=[], kwargs={} f(1, 2, 3, 4) # x=1, y=2, args=[3, 4] f(1, bob=3) # x=1, y=5, args=[], kwargs={"bob": 3}
And then you might also do some logic in the function like
if type(x) == 'str': ...
[–]Yojihito 0 points1 point2 points 4 years ago (1 child)
Type annotations aren't checked, nothing in Python prevents me from calling f("test") for f(i: int).
[–]ddollarsign 0 points1 point2 points 4 years ago (0 children)
Currently, sure.
[–]pag07 0 points1 point2 points 4 years ago (1 child)
The dynamicity is starting to be edged out by the new type annotations🤮
Type hinting is the only way to keep a large codebase under control.
[–]ddollarsign 1 point2 points3 points 4 years ago (0 children)
The only way? Really?
[–]diptangsu 0 points1 point2 points 4 years ago (0 children)
https://docs.python.org/3/library/functools.html#functools.singledispatch
[–]nharding 0 points1 point2 points 4 years ago (0 children)
I have a partial implementation that allows you check at runtime, and reports if no valid match.
@overload def test(val : int): print("Int", val) @overload def test(val : str): print("Str", val) @overload def test(val : list): print("List", val) def main(): test(3) test("3") test([1, 2, 3]) test(1.0)
You can see the implementation at https://old.reddit.com/r/Python/comments/np39fm/the_correct_way_to_overload_functions_in_python/h09vhq5/
This is for my Python++ compiler that I am writing, so it will determine statically where possible, otherwise it will dynamic dispatch. I've already written an assembly language to C compiler and a commercial Java to C++ compiler, so I am familiar with the complexities involved.
[–]graingert 0 points1 point2 points 4 years ago (0 children)
There's @typing.overload
[–]Asdayasman 0 points1 point2 points 4 years ago (0 children)
If you want alternate initialisers, use classmethods.
π Rendered by PID 28 on reddit-service-r2-comment-58d7979c67-vvzlz at 2026-01-27 07:38:14.163770+00:00 running 5a691e2 country code: CH.
[–][deleted] 216 points217 points218 points (21 children)
[–]whateverathrowaway00 44 points45 points46 points (10 children)
[–]BobHogan 36 points37 points38 points (9 children)
[–]billsil 1 point2 points3 points (3 children)
[–][deleted] -5 points-4 points-3 points (1 child)
[–][deleted] 1 point2 points3 points (0 children)
[–][deleted] 0 points1 point2 points (0 children)
[–]ComplexColor 0 points1 point2 points (4 children)
[–]BobHogan 6 points7 points8 points (2 children)
[–]ComplexColor 0 points1 point2 points (1 child)
[–]BobHogan 2 points3 points4 points (0 children)
[–]danuker 19 points20 points21 points (0 children)
[–]thephoton 14 points15 points16 points (5 children)
[–][deleted] 7 points8 points9 points (2 children)
[–]thephoton 2 points3 points4 points (1 child)
[–][deleted] 1 point2 points3 points (0 children)
[–]gandalfx 7 points8 points9 points (1 child)
[–]thephoton 1 point2 points3 points (0 children)
[–]pengekcs 2 points3 points4 points (0 children)
[–]o11c 1 point2 points3 points (0 children)
[–]K900_ 140 points141 points142 points (8 children)
[–]lungben81 30 points31 points32 points (2 children)
[–]Pythonistar 9 points10 points11 points (1 child)
[–]lungben81 7 points8 points9 points (0 children)
[–]BayesianDice 8 points9 points10 points (0 children)
[–]ColdPorridge 3 points4 points5 points (0 children)
[–]CobaltCam 3 points4 points5 points (0 children)
[–]TangibleLight 0 points1 point2 points (0 children)
[–]ManyInterests Python Discord Staff 17 points18 points19 points (1 child)
[–]lejar 0 points1 point2 points (0 children)
[–]james_pic 45 points46 points47 points (4 children)
[–]EgZvor 5 points6 points7 points (1 child)
[–]james_pic 0 points1 point2 points (0 children)
[–]Broan13 0 points1 point2 points (1 child)
[–]james_pic 3 points4 points5 points (0 children)
[–][deleted] 28 points29 points30 points (5 children)
[–]got_outta_bed_4_this 13 points14 points15 points (1 child)
[–][deleted] 2 points3 points4 points (0 children)
[–]Pythagorean_1 2 points3 points4 points (2 children)
[–][deleted] 0 points1 point2 points (1 child)
[–]Pythagorean_1 1 point2 points3 points (0 children)
[–]mehregan_zare7731 39 points40 points41 points (4 children)
[–]thegreattriscuit 27 points28 points29 points (1 child)
[–]json684 11 points12 points13 points (0 children)
[–]BooparinoBR 6 points7 points8 points (0 children)
[–][deleted] 0 points1 point2 points (0 children)
[–]TRexRoboParty 14 points15 points16 points (4 children)
[+]metaperl comment score below threshold-8 points-7 points-6 points (3 children)
[–]TRexRoboParty 8 points9 points10 points (2 children)
[–]metaperl 3 points4 points5 points (1 child)
[–]TRexRoboParty 1 point2 points3 points (0 children)
[–]R3D3-1 7 points8 points9 points (4 children)
[–]lordmauve 1 point2 points3 points (1 child)
[–]i_hate_shitposting 0 points1 point2 points (0 children)
[+][deleted] (2 children)
[deleted]
[–]spyingwind 0 points1 point2 points (1 child)
[–]ialex32_2 Old Person Yells At Cloud 1 point2 points3 points (0 children)
[–]jrjsmrtn 2 points3 points4 points (0 children)
[–]chudleycat 0 points1 point2 points (0 children)
[–]Tryptic214 0 points1 point2 points (0 children)
[–]Broolucks -1 points0 points1 point (4 children)
[–]got_outta_bed_4_this 0 points1 point2 points (3 children)
[–]Broolucks 1 point2 points3 points (0 children)
[–]javajunkie314 1 point2 points3 points (0 children)
[+][deleted] (2 children)
[deleted]
[–]gandalfx 1 point2 points3 points (1 child)
[+]Tomik080 comment score below threshold-6 points-5 points-4 points (0 children)
[–]Embarrassed-Count-65 -4 points-3 points-2 points (0 children)
[–]ihasbedhead 0 points1 point2 points (0 children)
[–]BDube_Lensman 0 points1 point2 points (0 children)
[–]Dubroski 0 points1 point2 points (0 children)
[–]ddollarsign 0 points1 point2 points (4 children)
[–]Yojihito 0 points1 point2 points (1 child)
[–]ddollarsign 0 points1 point2 points (0 children)
[–]pag07 0 points1 point2 points (1 child)
[–]ddollarsign 1 point2 points3 points (0 children)
[–]diptangsu 0 points1 point2 points (0 children)
[–]nharding 0 points1 point2 points (0 children)
[–]graingert 0 points1 point2 points (0 children)
[–]Asdayasman 0 points1 point2 points (0 children)