4 ms·
This got missed from the release announcement, but now there's `functools.singledispatchmethod`,[1] as the class method sibling to `functools.singledispatch`.[2
by Xophmeister 7y ago
This got missed from the release announcement, but now there's `functools.singledispatchmethod`,[1] as the class method sibling to `functools.singledispatch`.[2] This allows you to overload the implementation of a function (and now a method) based on the type of its first argument. This saves you writing code like:
def foo(bar):
if isinstance(bar, Quux):
# Treat bar as a Quux
elif isinstance(bar, Xyzzy):
# Treat bar as an Xyzzy
# etc.
I understand runtime type checking like that is considered a bit of a Python antipattern. With `singledispatch`, you can do this instead:
@singledispatch
def foo(bar:Quux):
# Quux implementation
@foo.register
def _(bar:Xyzzy):
# Xyzzy implementation
With `singledispatchmethod`, you can now also do this to class methods, where the type of the first non-self/class is used by the interpreter to check the type, based on its annotation (or using the argument to its `register` method). You could mimic this behaviour using `singledispatch` in your constructor, but this syntax is much nicer.
[1] https://docs.python.org/3/library/functools.html#functools.singledispatchmethod https://docs.python.org/3/library/functools.html#functools.s...
[2] https://docs.python.org/3/library/functools.html#functools.singledispatch https://docs.python.org/3/library/functools.html#functools.s...
- bouncing 7y agoI can definitely imagine some places where this replaces type-checking, but it still seems like a bit of an unfortunate anti-pattern to me, since it's really a sort of C/C++ style function prototype match. My immediate thought is that it's going to be hard for PyCharm to reliably point me to a function definition.
- heavenlyblue 7y ago>> My immediate thought is that it's going to be hard for PyCharm to reliably point me to a function definition. I am certain PyCharm is going to special-case these decorators in their next release.
- gimboland 7y agoNice. I missed this feature only a few weeks ago. Good to know it's landed!
- dfox 7y agoFew years ago I wrote this thing: https://github.com/adh/py-clos https://github.com/adh/py-clos It can also do dispatch on multiple arguments and on equality and has dispatch cache implemented as C extension (from my rudimentary measurements it seems that dispatch with cache hit is actually slightly faster than normal CPython method call).
- hirowan 7y agoThe issue with this and `singledispatch` is that they no longer support pseudo-types from the `typing` module [1] so you can't use them with containers of type `x`, e.g. `List[str]`, or protocols like `Sequence`. [1] https://bugs.python.org/issue34498 https://bugs.python.org/issue34498
- spott 7y agoYea, this one is really annoying... required a lot of rewriting of code to make some old code work with 3.7.
- xamuel 7y agoI don't understand why anyone would want singledispatch. Instead of having the function defined in one place where you can look it up, now the function is potentially scattered all over the place. (I'm not talking hypothetically. I've had the 'pleasure' of working on a codebase where different singledispatch cases of the same function were defined in different files!)
- CreRecombinase 7y agoBecause if you need to branch based on type more than a few times in your function it can get pretty hard to read. Am I right that the basis of your complaint is that it's now harder to find all the members of (what you'd call in C++) the "overload set" for a particular function? If so I can see your point.
- xamuel 7y agoYes, that's my point. And it doesn't really simplify the function itself, it just rearranges it, in the same way you can break up a complex painting into jigsaw pieces and say, "Look, each piece is simple!"
- CreRecombinase 7y agoI think the answer to the question "should I apply X refactoring technique to this function Y?" obviously has to depend on both X and Y. There's clearly a trade-off here. If I have a free function foo(x) that I want to work differently depending if x is type A or type B, splitting that means breaking up foo, sure, but because I can only ever call foo with an x of type A or type B (unless I'm converting some As to Bs halfway through foo or something awful like that) it might be useful to see all of the "A" logic in one place and all of the "B" logic in another place.