I want my __new__ method to behave differently in some cases and wanted to split it into overloaded functions with singledispatchmethod.
However this does not work, the overloading functions are never called. What is the reason for that?
from functools import singledispatchmethod
class Foo:
@singledispatchmethod
def __new__(cls, arg1, **kwargs):
return "default call"
@__new__.register(int)
def _(cls, arg1, **kwargs):
return "called with int " + str(arg1)
print(Foo("hi"))
# default call
print(Foo(1))
# default call
As an experiment also used singledispatch instead but without success.
You might think that
singledispatchmethodis just a version ofsingledispatchthat dispatches based on the second argument instead of the first, but that's not how it works.Instead, it's written as a class that implements the descriptor protocol to customize attribute access. When you access a
singledispatchmethod-decorated method, the attribute access returns a closure object that dispatches based on the first argument to that closure object.So for example, if a class named
Examplehad a singledispatchmethod namedsdm, thenExample().sdm(1)would dispatch on the type of 1, butExample.sdm(Example(), 1)would dispatch on the type ofExample()!__new__isn't a regular method. It's supposed to be a staticmethod, or at least, it's supposed to be something that behaves like a staticmethod when accessed. (Ordinarily,type.__new__would automatically convert__new__methods to staticmethods, but it only does that when__new__is an ordinary Python function object, and as mentioned,singledispatchmethodis implemented as a custom class.)Particularly, when you do
Foo(1), the resulting__new__invocation works likeFoo.__new__(Foo, 1). It retrieves the__new__attribute onFoo, then calls whatever it finds withFooand1as arguments.Due to the way
singledispatchmethodperforms dispatch, this dispatches based on the type ofFoo, not the type of1.Ordinarily, if you wanted staticmethod-like behavior from
singledispatchmethod, the way to get it would be to take advantage of asingledispatchmethodfeature that lets it wrap other decorators, like so:However, this doesn't do anything to fix the problem of which argument we're dispatching on.
Instead, you could write
__new__as a regular method that delegates to asingledispatchhelper, and reorder the helper's arguments so the argument you want to dispatch on is in front:Or, of course, you could ditch the whole singledispatch thing and just write the dispatch handling yourself: