我想知道是否可以实现以下目标,如果可以,不需要太多额外的代码:
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 中的模式匹配优雅地完成。那么,如果目前不可行,也许我应该等待它的推出和成熟?