0

我想知道是否可以实现以下目标,如果可以,不需要太多额外的代码:

from __future__ import annotations
from functools import singledispatch

@singledispatch
def somefunc(value):
    print(f"Type {type(value).__qualname__!r} "
          f"is not registered for dispatch.")

@somefunc.register
def _(value: list[int]):
    print(f"Dispatched type list[int]!")

@somefunc.register
def _(value: list[str]):
    print(f"Dispatched type list[str]!")

somefunc('123')
somefunc([123])
somefunc(list('123'))

并获得输出:

Type 'str' is not registered for dispatch.
Dispatched type list[int]!
Dispatched type list[str]!

但是,使用 python 3.9.6 运行此代码段会导致 functools.py 的第 742 行出现错误:

TypeError: issubclass() argument 2 cannot be 
a parameterized generic

singledispatch用户定义的类一样,实现这项工作的一种方法是对传递的列表中的元素进行类型检查,将传递的列表包装到一个表示 eg 的类中list[str],并让调度的函数再次调用自身,并将新实例作为参数:

from __future__ import annotations
from functools import singledispatch


class ListOfStrs(list):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)


class ListOfInts(list):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)


@singledispatch
def somefunc(value):
    print(f"Type {type(value).__qualname__!r} "
          f"is not registered for dispatch.")


@somefunc.register
def _(value: list):
    if value and all(isinstance(subval, int) 
                     for subval in value):
        somefunc(ListOfInts(value))

    elif value and all(isinstance(subval, str) 
                       for subval in value):
        somefunc(ListOfStrs(value))

    else:
        print(
            f"Dispatched a list whose elements ",
            f"are not all of a registered type."
            )


@somefunc.register
def _(value: ListOfStrs):
    print(f"Dispatched type 'list[str]'!")


@somefunc.register
def _(value: ListOfInts):
    print(f"Dispatched type 'list[int]'!")


somefunc('123')
somefunc([1, 2, 3])
somefunc(list('123'))
somefunc([{1}, {2}, {3}])

正如预期的那样,结果是:

Type 'str' is not registered for dispatch.
Dispatched type 'list[int]'!
Dispatched type 'list[str]'!
Dispatched a list whose elements are 
 not all of a registered type.

然而,除了增加可扩展性之外,首先使用singledispatch的一个原因是规避冗长的类型检查,有些人认为这是一种反模式。当然,特别是对于这个解决方案,您需要定义乱扔代码的包装类(可能有更好的方法来实现这一点,我目前还没有看到,但第一点仍然存在)。

一个原因可能是这里避免了对“列表”的一种类型检查,但这仅通过一个 if/else 子句就降低了复杂性。

所以我实际上不会使用最后一种情况。

有谁知道如何像使用非复合类型一样优雅地获得这种行为?

我想这可以使用 3.10 中的模式匹配优雅地完成。那么,如果目前不可行,也许我应该等待它的推出和成熟?

4

0 回答 0