Nay*_*uki 0 python type-hinting
我有这个通用功能:
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]
Run Code Online (Sandbox Code Playgroud)
我们可以看到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]
Run Code Online (Sandbox Code Playgroud)
它产生以下错误消息:
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)
Run Code Online (Sandbox Code Playgroud)
我正在运行 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]
Run Code Online (Sandbox Code Playgroud)
注意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]
Run Code Online (Sandbox Code Playgroud)
使用这种类型不需要强制转换,并且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"
Run Code Online (Sandbox Code Playgroud)
dict 参数(实现__getitem__
但不使用切片作为索引)和根本没有实现的类型会产生类似的失败__getitem__
。