我希望我的
__new__
方法在某些情况下表现不同,并希望使用 singledispatchmethod
将其拆分为重载函数。
然而这不起作用,重载函数永远不会被调用。这是什么原因呢?
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
作为实验也使用
singledispatch
代替但没有成功。
您可能认为
singledispatchmethod
只是 singledispatch
的一个版本,它根据第二个参数而不是第一个参数进行调度,但事实并非如此。
相反,它是书面作为实现描述符协议来自定义属性访问的类。当您访问
singledispatchmethod
修饰的方法时,属性访问返回一个闭包对象,该对象根据该闭包对象的第一个参数进行分派。
例如,如果名为
Example
的类有一个名为 sdm
的单一调度方法,那么 Example().sdm(1)
将调度 1 的类型,但 Example.sdm(Example(), 1)
将调度 Example()
! 的类型
__new__
不是常规方法。它应该是一个静态方法,或者至少,它应该是在访问时表现得像静态方法的东西。 (通常,type.__new__
会自动将__new__
方法转换为静态方法,但只有当__new__
是普通Python函数对象时才会这样做,并且如上所述,singledispatchmethod
是作为自定义类实现的。)
特别是,当您执行
Foo(1)
时,生成的 __new__
调用的工作方式类似于 Foo.__new__(Foo, 1)
。它检索 __new__
上的 Foo
属性,然后使用 Foo
和 1
作为参数调用它找到的任何内容。
由于
singledispatchmethod
执行调度的方式,此调度基于 Foo
的类型,而不是 1
的类型。
通常,如果您想要
singledispatchmethod
具有类似于静态方法的行为,获得它的方法是利用 singledispatchmethod
功能,让它包装其他装饰器,如下所示:
# This doesn't do what you need.
@functools.singledispatchmethod
@staticmethod
def __new__(cls, arg1, **kwargs):
...
@__new__.register(int)
@staticmethod
def _(cls, arg1, **kwargs):
...
但是,这并不能解决我们正在分派哪个参数的问题。
相反,您可以将
__new__
编写为委托给 singledispatch
帮助器的常规方法,并重新排序帮助器的参数,以便您要分派的参数位于前面:
import functools
class Foo:
def __new__(cls, arg1, **kwargs):
return _new_helper(arg1, cls, **kwargs)
@functools.singledispatch
def _new_helper(arg1, cls, **kwargs):
return "default call"
@_new_helper.register(int)
def _(arg1, cls, **kwargs):
return "called with int " + str(arg1)
或者,当然,您可以放弃整个 singledispatch 的事情,而自己编写调度处理:
import functools
class Foo:
def __new__(cls, arg1, **kwargs):
if isinstance(arg1, int):
return "called with int " + str(arg1)
else:
return "default call"