如何使用 singledispatchmethod 重载 __new__ 以及它不能按预期工作的原因是什么?

问题描述 投票:0回答:1

我希望我的

__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
代替但没有成功。

python initialization functools single-dispatch
1个回答
3
投票

您可能认为

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"
© www.soinside.com 2019 - 2024. All rights reserved.