Closed adam-urbanczyk closed 2 years ago
That's supported. Registering a function with no annotations - or using explicit .register()
with no args - will inherently match all calls.
Great, thanks! Could it work with annotations and methods? Or is it needed to add a dummy method then to handle the default case? I'm essentially trying to get something like this:
class A:
@multimethod(default=True)
def f(self, a: int):
print(a)
Could it work with annotations and methods? Or is it needed to add a dummy method then to handle the default case?
That's what I'd recommend, just because I don't understand the use case. Is a
supposed to be an int
or not?
But if you want, explicitly registering the types will ignore annotations.
@f.register() # no types given
def _(self, a: int):
print(a)
So the use case is as follows: a large codebase starts to use multimethods, but there is a small group of users that relied on positional arguments used as keyword arguments., so it would be great if the original method (which btw already had type annotations) could be also marked as a default one so that any call relying on keyword args would end up being dispatched to that method.
In the example above I'd like that both f(1)
and f(a=1)
worked without an additional _
method.
In the example above I'd like that both
f(1)
andf(a=1)
worked without an additional_
method.
Ok, but in that case f(1.0)
would also match. There's another option: multidispatch
now supports dispatching on keyword arguments.
class A:
@multidispatch
def f(self, a: int):
print(a)
A().f(0)
A().f(a=0)
I somehow assumed that it is not meant for methods. Thanks, looks like the solution!
I might have jumped to conclusions too quickly. multidispatch
does not seem to like changing signatures. Here is a more complete use case I'm after:
from multimethod import multimethod, multidispatch
class A:
@multimethod
def f(self, a: int, c: str='s'):
print(1)
@multimethod
def f(self, a: int, b: int, c: str='b'):
print(2)
@multimethod
def f(self, *args, **kwargs):
_f = next(iter(self.f.values()))
return _f(self, *args, **kwargs)
A().f(0,'s')
A().f(0)
A().f(0,c='s')
A().f(0,1,c='s')
A().f(0,1,'s')
A().f(0,1)
A().f(a=0,c='s') #prints 1
In essence, I want to get rid of the explicit definition of the third method, but still want to keep the behavior. Do I need to subclass multimethod
or is such a case already supported?
multidispatch
does not seem to like changing signatures.
Yes, it uses the base implementation for performance. I don't see a way that it could handle different signatures without doing a linear scan through all the functions, which is what overload
does.
Speaking of which, this example would be called "overloading" in other languages, because the signatures are varying, not the types. So one question is should both functions have the same name, or conversely should there be one function where b
is optional?
I think extending overload
to allow types as well as predicates is reasonable. Then this would work:
@overload
def f(self, a: int, c: str='s'):
print(1)
@overload
def f(self, a: int, b: int, c: str='b'):
print(2)
Thanks, sounds like a good solution!
Would it be in scope of multimethod to fallback to one of the methods (possibly explicitly marked) in case of a dispatch error?
It would help to maintain backward compatibility for our codebase for people specifying positional arguments with keywords.