如何将singledispatchmethod与继承类一起使用

2024-09-28 22:34:04 发布

您现在位置:Python中文网/ 问答频道 /正文

在我的代码中,我有以下类:

class A:
  @functools.singledispatchmethod
  def handle(arg):
     pass

我希望其他类从A继承并重载泛型方法handle,如下所示:

class B(A):
   @handle.register
   def handle_int(arg: int):
       return arg + 2

但是,我得到一个错误:

unresolved reference 'handle'

如何在基类中创建此泛型方法?(我不想在每个子类中创建这个函数来使用singledispatchmethod。)


Tags: 方法代码registerreturndef错误argpass
2条回答

要在Python中实现您想要做的事情有点困难singledispatch和{}本身都是该语言中相对较新的特征。据我所知,目前还没有特别支持更复杂的重载,例如您正在尝试的重载

话虽如此,您可以使用第三方multipledispatchmodule尝试下面的方法。不过,这感觉有点像黑客,我不知道如何让它在类方法上工作——下面的解决方案只对实例方法有效

from multipledispatch import dispatch

@dispatch(object, object)
def handle(instance, arg):
    return 'Base implementation'
        
class A:
    def handle(self, arg):
        return handle(self, arg)

class B(A):
    pass

class C(A):
    pass

@dispatch(B, int)
def handle(instance, arg):
    return 'Specialised implementation for class B and ints'

@dispatch(C, str)
def handle(instance, arg):
    return 'Specialised implementation for class C and strs'

a, b, c = A(), B(), C()

print(a.handle('hi')) # prints "Base implementation"
print(b.handle('hi')) # prints "Base implementation"
print(b.handle(3)) # prints "Specialised implementation for class B and ints"
print(c.handle(3)) # prints "Base implementation"
print(c.handle('hi')) # prints "Specialised implementation for class C and strs"

使用pip上的另一个第三方模块plum-dispatchmodule,您可能会更接近您想要的结果。我对它了解不多,但我认为它有一些multipledispatch没有的额外特性

不是理想的方法

由于您引用的是类A中定义的方法,因此必须使用@A.handle.register来指示它:

class B(A):
   @A.handle.register
   def handle_int(arg: int):
       return arg + 2

问题

但是,当存在另一个类C时,这种方法会导致问题,该类也继承自A,但支持handle(arg: str)。然后C().handle(2)将从类B调用方法,因为它已注册到A方法(即使它最终应该是类A基句柄方法)

更好的方法

上述解决方案的明显问题是一个注册类(A),因此我将在所有派生类中添加注册,但如果派生类中没有适当的类型专用类方法,则将处理留给基类

import functools

class A:
  @functools.singledispatchmethod
  def handle(arg):
     print(f'\tA handle (arg: {arg})')

class B(A):
    @functools.singledispatchmethod
    @classmethod
    def handle(cls, arg):
        print(f'\tB handle (arg: {arg})')
        return super(B, cls).handle(arg)


@B.handle.register
def handle_int(arg: int):
    print(f'\tB int (arg: {arg})')
    return arg + 2


class C(A):
    @functools.singledispatchmethod
    @classmethod
    def handle(cls, arg):
        print(f'\tC handle (arg: {arg})')
        return super(C, cls).handle(arg)

@C.handle.register
def handle_str(arg: str):
    print(f'\tC str (arg: {arg})')
    return arg + ' 2'

print('\nA')
A.handle(2)
A.handle('2+')

print('\nB')
B.handle(2)
B.handle('2+')

print('\nC')
C.handle(2)
C.handle('2+')

结果:

A
    A handle (arg: 2)
    A handle (arg: 2+)

B
    B int (arg: 2)
    B handle (arg: 2+)
    A handle (arg: 2+)

C
    C handle (arg: 2)
    A handle (arg: 2)
    C str (arg: 2+)

相关问题 更多 >