我想表示第一个参数是一个"列表";并且结果具有与第二参数相同的类型。
这个mysum
(即不是标准的libsum
(应该与int/foat/str/list以及任何其他支持+=
的类型同样适用。
天真:
def mysum(lst: list[T], start: T) -> T:
x = start
for item in lst:
x += item
return x
其产生:
(dev311) go|c:srvtmp> mypy sumtype.py
sumtype.py:26: error: Unsupported left operand type for + ("T")
sumtype.py:35: error: Cannot infer type argument 1 of "mysum"
Found 2 errors in 1 file (checked 1 source file)
第二次尝试:
from typing import Iterable, Protocol, TypeVar
T = TypeVar('T')
class Addable(Protocol[T]):
def __add__(self, other: T) -> T:
...
class RAddable(Protocol[T]):
def __radd__(self, other: T) -> T:
...
def mysum(lst: Iterable[Addable|RAddable], start: Addable) -> Addable:
x = start
for item in lst:
x += item
return x
然而,这并没有对";列表";项,并且开始是相同的类型,因此此类型检查:
class Foo:
def __radd__(self, other: int) -> int:
return other + 42
mysum([Foo()], []) # <=== should ideally fail since Foo() and [] are not the same type
并在运行时出现类型错误而失败:
(dev311) go|c:srvtmp> python sumtype.py
Traceback (most recent call last):
File "c:srvtmpsumtype.py", line 27, in <module>
mysum([Foo()], [])
File "c:srvtmpsumtype.py", line 18, in mysum
x += item
File "c:srvtmpsumtype.py", line 24, in __radd__
return other + 42
~~~~~~^~~~
TypeError: can only concatenate list (not "int") to list
我假设对泛型函数进行类型注释不会这么困难,那么我缺少什么。。?
您可以将泛型类型绑定到支持__add__
的协议:
类型变量可以绑定到具体类型、抽象类型(ABC或协议(,甚至类型的联合
from typing import TypeVar, Protocol
T = TypeVar('T', bound='Addable')
class Addable(Protocol):
def __add__(self: T, other: T) -> T:
...
def mysum(lst: list[T], start: T) -> T:
x = start
for item in lst:
x += item
return x
演示:https://mypy-play.net/?gist=ecf4031f21fb81e75d6e6f75d16f3911
我想您在第二次尝试时忘记了将类型参数传递给(R)Addable
。
from typing import Iterable, Protocol, TypeVar
T = TypeVar('T')
class Addable(Protocol[T]):
def __add__(self: T, other: T) -> T:
...
class RAddable(Protocol[T]):
def __radd__(self: T, other: T) -> T:
...
def mysum(lst: Iterable[Addable[T]|RAddable[T]], start: Addable[T]) -> T:
x = start
for item in lst:
x += item
return x
但这也行不通。使用@blhsing建议的有界TypeVar比这更优雅、更直观。
mypy中唯一可以在没有绑定TypeVar:的情况下获得的工作示例
from typing import Iterable, Protocol, TypeVar
T = TypeVar('T')
class Addable(Protocol[T]):
def __add__(self: T, other: T) -> T: ...
def mysum(lst: Iterable[Addable[T]], start: T) -> T:
x = start
for item in lst:
x = item + x
return x
print(mysum(['a'], 'b'))
str
不是mypy
中的RAddable[T]
,int
正常:
from typing import Iterable, Protocol, TypeVar
T = TypeVar('T')
class RAddable(Protocol[T]):
def __radd__(self: T, other: T) -> T: ...
def mysum(lst: Iterable[RAddable[T]], start: T) -> T:
x = start
for item in lst:
x += item
return x
print(mysum([1], 2))