切片的Python类型注释?
我有这个通用功能:
def slice(seq):
return seq[1:3]
assert slice("abcde") == "bc"
assert slice(b"xyz") == b"yz"
assert slice([2,7,1,8]) == [7,1]
我们可以看到slice有类型str -> str, bytes -> bytes, List[int] -> List[int]。
如何为函数编写类型注释?这是一个错误的尝试(TypeVar文档):
from typing import *
E = TypeVar("E")
T = TypeVar("T", bytes, str, Sequence[E])
def slice(seq: T) -> T:
return seq[1:3]
它产生以下错误消息:
error: Type variable "mymodule.E" is unbound
note: (Hint: Use "Generic[E]" or "Protocol[E]" base class to bind "E" inside a class
note: (Hint: Use "E" in function signature to bind "E" inside a function)
我正在运行 Python 3.9.6 和 mypy 0.910。
回答
你想TypeVar用bound的Sequence。
from typing import Sequence, TypeVar, cast
_T = TypeVar("_T", bound=Sequence)
def slice(seq: _T) -> _T:
return cast(_T, seq[1:3])
assert slice("abcde") == "bc"
assert slice(b"xyz") == b"yz"
assert slice([2, 7, 1, 8]) == [7, 1]
注意cast内部slice是必要的,因为实际上并不保证切片运算符(至少从 Python 3.8 开始,根据Sequence抽象类)返回与原始对象相同的类型。
你可以通过定义你自己的Slicable协议来解决这个问题并拥有更强的类型(你需要停止隐藏内置slice类型):
from typing import Protocol, TypeVar
_S = TypeVar('_S', bound='Slicable')
class Slicable(Protocol):
def __getitem__(self: _S, i: slice) -> _S: ...
_T = TypeVar('_T', bound=Slicable)
def my_slice(seq: _T) -> _T:
return seq[1:3]
assert my_slice("abcde") == "bc"
assert my_slice(b"xyz") == b"yz"
assert my_slice([2, 7, 1, 8]) == [7, 1]
使用这种类型不需要强制转换,并且my_slice只允许其类型实现返回相同类型对象的切片操作的参数。例如,如果我定义一个__getitem__返回任意字符串的类:
class TrickySlicable:
def __getitem__(self, i: slice) -> str:
return "foo"
my_slice(TrickySlicable()) # error: Value of type variable "_T" of "my_slice" cannot be "TrickySlicable"
dict 参数(实现__getitem__但不使用切片作为索引)和根本没有实现的类型会产生类似的失败__getitem__。