python是一门动态语言,若编码不规范会出现很多运行时错误。为了提高代码可读性,减少运行时bug,python提供了类型注释可以对函数参数,返回值,变量等进行标注。通过静态检查就可以发现一些常见的问题。类型注释的代码在python运行时并不会触发,所以没有性能影响。
1.类型注释的好处,看如下这个例子。
这两种方法在python3.5之后都是正确的写法,一点问题都没有。但是看左边的代码就会遇到的问题,因为python是动态语言,函数参数name
的类型只有在运行时才知道。如果我们这样调用左边的函数gretting(["world"])
,这很明显字符串不能和列表相加,这在运行时就会发生异常。
如果我们使用右边的写法通过静态检查器就可以提前检查出错误。右边提示了参数name
的类型为str,函数的返回值为str。
通过mypy
对代码进行类型检查,可以看到参数传递错误被检查出来了。
2.python提供的类型注释
python从3.5版本开始将类型注释并入了标准库中。可通过如下方式引用from typing import List, Dict
对变量进行注释
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18In [1]: x: int = 1 # 表示变量x为int类型
In [2]: x: float = 0.1 # 表示变量x为float类型
In [3]: x: bool = True # 表示变量x为bool类型
In [4]: x: str = "test" # 表示变量x为str类型
In [5]: x: bytes = b"test" # 表示变量x为bytes类型
In [8]: from typing import List,Dict,Sequence,Tuple,Set
In [9]: x: Dict[str, float] = {'field': 2.0} # x是一个字典key为str类型,值为float类型
In [10]: x: Tuple[int, str, float] = (3, "yes", 7.5) # x是一个元组,元素类型可以为int, str, float
In [11]: x: List[int] = [1] # x是一个列表,元素类型为int
In [12]: x: Set[int] = {6, 7} # x是一个集合,元素类型为int
In [16]: x: Union[int,str] = 1 # x类型可以为int或者str
In [17]: x: Union[int, None] = 1 # x类型可以为int或者None,等同于Optional[int]
In [18]: x: Optional[int] = 1函数注释
1
2def gretting(name: str) -> str:
return "Hello " + name
1 | # 对一个可调用的函数进行注释 |
- 类变量
1
2
3
4
5
6
7
8
9
10
11class Car:
seats: ClassVar[int] = 4 # seats是一个类变量,类型为int
def f(x: 'A') -> None: # OK # 表示函数f的参数x的类型为A的实例
...
class A:
...
x: A = A() # 表示x的类型为A的实例,若想表示x的类型为类对象,请看下面
x: Type[A] = A
typing模块中的协议类
协议 | 方法 |
---|---|
Iterable[T] |
def __iter__(self) -> Iterator[T] |
Iterator[T] |
def __next__(self) -> T def __iter__(self) -> Iterator[T] |
Sized |
def __len__(self) -> int |
Container[T] |
def __contains__(self, x: object) -> bool |
Collection[T] |
def __len__(self) -> int def __iter__(self) -> Iterator[T] def __contains__(self, x: object) -> bool |
Awaitable[T] |
def __await__(self) -> Generator[Any, None, T] |
AsyncIterable[T] |
def __aiter__(self) -> AsyncIterator[T] |
AsyncIterator[T] |
def __anext__(self) -> Awaitable[T] def __aiter__(self) -> AsyncIterator[T] |
ContextManager[T] |
def __enter__(self) -> T def __exit__(self,exc_type: Optional[Type[BaseException]],exc_value: Optional[BaseException],traceback: Optional[TracebackType]) -> Optional[bool] |
AsyncContextManager[T] |
def __aenter__(self) -> Awaitable[T] def __aexit__(self,exc_type:Optional[Type[BaseException]],exc_value: Optional[BaseException],traceback: Optional[TracebackType]) -> Awaitable[Optional[bool]] |
cast 将静态类型值强制转换为子类型,并不是像其它静态语言真正的进行类型转换,在运行时并不生效。
1
2
3
4
5from typing import cast, List
o: object = [1]
x = cast(List[int], o) # OK
y = cast(List[str], o) # OK (cast performs no actual runtime check)Generics
python内置的collection classes
都是generic classes
。Generic types拥有一个或多个类型参数。如Dict[int, str]
List[int]
。
自定义一个原生类:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17from typing import TypeVar, Generic
T = TypeVar('T')
class Stack(Generic[T]):
def __init__(self) -> None:
# Create an empty list with items of type T
self.items: List[T] = []
def push(self, item: T) -> None:
self.items.append(item)
def pop(self) -> T:
return self.items.pop()
def empty(self) -> bool:
return not self.items
现在Stack
类可以用于表示拥有任务类型的stack
,如Stack[int],Stack[str],Stack[Tuple[int, str]]
等。
可以像使用内置容器类型一样使用类Stack
1
2
3
4
5# Construct an empty Stack[int] instance
stack = Stack[int]()
stack.push(2)
stack.pop()
stack.push('x') # Type error
- Variance of generic types
关于它们之间的子类型关系,存在三种主要类型的通用类型:invariant
(不变的),covariant
(协变的),contravariant
(逆变的)
1 | class Animal: |
如上,Animal
有子类型Cat
,Cat
可能可以使用Animal
中的某些方法,这个可能取决于类型是covariant
或者contravariant
。
如果Cat
可以使用它的超类型Animal
中的一些方法,这种情况被称为convariant
;如果超类型Animal
可以使用子类型Cat
中的某些方法,则被称为contravariant
。
如果convariant
和contravariant
都不成立,则被称作invariant
,即Cat
不能使用Animal
中的方法,Animal
也不能使用Cat
中的方法。
1 | from typing import TypeVar, Generic, Iterable, Iterator |
当contravariant=True
时,使用命令mypy co.py
,出现如下错误:
默认情况下mypy
认为所有用户自定义的原生类型都是invariant
的。
请试下当convariant=True
时又是怎样的结果
Ref:
1.pep484
2.pep526
3.mypy
4.https://blog.magrathealabs.com/pythons-covariance-and-contravariance-b422c63f57ac