typing
基础概念
- class 类本身能作为类型作为类型提示使用
基础类型
- Tuple
- List
- int
- float
- str
- bool
类型别名
TypeAlias
from typing import TypeAlias
Factors: TypeAlias = list[int]
新建类型
NewType
某些类型会变得非常复杂,或者使用别名会提高代码可读性时,类型别名是非常有用的技巧, 以下是文档中的例子。通过类型别名定义了 UserId
,而且 ProUserId
也能从 UserId
中 派生而来。
from typing import NewType
UserId = NewType('UserId', int)
ProUserId = NewType('ProUserId', UserId)
UserId = NewType('UserId', int)
# 等价于
class UserId(int): pass
# 运行时会编译成
def UserId(x): return x
from typing import List
Scores = List[float]
# 如果这里的类型很长 Tuple[int, float, str, bool],甚至可以多类型组合
# 和 score: List[float]是一样的
def get_sum_score(scores: Scores) -> float:
"""获取总分数"""
print(sum(scores))
return sum(scores)
自定义类型
from typing import NewType
UserId = NewType("UserId", int)
def filter_user_name(user_id: UserId) -> str:
"""根据用户id获取用户名"""
print(user_id)
return ""
# 这里必须是 UserId() 否则就不符合类型检查
filter_user_name(UserId(123))
# filter_user_name(123) 无法通过类型检查
字符串
字典
字典定义类型的方式有好几种,下面统一列出
from tpying import
Dict, # 限制Dict[str,int],类型结构跟map类似,一般用于map类型或者简单的字典
TypedDict,# 可以定义复杂的数据类型,需要使用 class 继承并独立声明
- Dict
# 这种方式只能统一限制key和value的类型,对于下面的结构并不非常合适
data:Dict[str,int] = {
'name':'capsion',
'age':'18'
}
- TypedDict
# 使用 TypedDict 可以非常明确的定义字典内不同key不同的类型,非常灵活
class Data(TypedDict):
name:str
age:int
使用这种方法声明类型后,使用它也非常灵活
# 下面几种方式均完全等价
data1:Data = {'name':'capsion','age':18}
data2 = Data(name='capsion', age=18)
data3:Data = dict(name='capsion', age=18)
print(data1 == data2 == data3) # True
甚至可以使用继承的方式扩展之前的类型
class Point2D(TypedDict):
x:int = 0
y:int = 0
class Point3D(Point2D):
z:int = 0
数组&元祖
from tpying import List,Tuple
def test(val:List[str, int], val2:Tuple[int, str]:
pass
# python3.9 之前
def test(val:List[Union[str, int]], val2:Tuple[Union[str, int]]:
pass
# python 3.9
def test(val:List[str | int], val2:Tuple[str | int]:
pass
应用场景:
- 基础使用
# 声明一个 int 列表
int_list: List[int] = [100, 100]
# 声明一个含有两个 int 元素的元组
corr_x_y: Tuple[int, int] = (1, 2)
# 可变长度的元祖,可以用 `...`
corr_var: Tuple[int, ...] = (1, 2, 3)
# int,str 等多种类型的列表/元祖
union_list: List[Union[int, str]] = [100, 'Good']
枚举Union
from tpying import Union
Union[int] 等价于 int
Union[Union[int, str], int] 等同于 Union[int, str]
def test(val1:Dict[str,int]):
pass
可选 Optional
可选类型,顾名思义,这是一个可以选择的类型
from typing import Optional
Optional[int] 等价与 Union[int, None] # Optional默认第二个参数为 None
# 告诉编译器,这个类型除了是int外,默认值可以是None
def test(tar:Optional[int] = None) -> bool:
pass
应用场景:
if
场景定义了如下函数,pyright
会报错,
def judge(a: int, b: int) -> str:
if a > b:
return "great"
因为函数中的 if 导致了函数返回值有可能为 None
,这与函数的类型提示不符,因此我们可以添加 else 分支返回字符串,修复这个报错。
但有时候函数就是有可能返回 None 或者某个值,了解 Rust 应该知道这时候可以用 Option<T>
来表示返回值类型。 Python 的类型提示也提供了类似的 Optional 类型。
def judge(a: int, b: int) -> Optional[str]:
if a > b:
return "great"
字面量 Literal
它表示类型有效值应该和字面量一样。我觉得它最有用的地方在于表示有些枚举值时非常简单。 比如文件操作时 r
, rb
, w
flag,定义一个 Enum 非常繁琐,但用下面的例子则非常简单方便
应用场景:
- 内置的open方法
from typing import Literal
MODE = Literal['r', 'rb', 'w', 'wb']
def open_helper(file: str, mode: MODE) -> str:
...
open_helper('/some/path', 'r') # 正确
open_helper('/other/path', 'typo') # pyright 报错
对象
函数
高阶函数 - Callable
Python 经常用到高阶函数,因此,如何在参数和返回值类型提示表达函数是经常会遇到的问题, 为此 typing 提供了 Callable
from typing import Callable,
def add(a: int, b: int) -> int:
return a + b
def apply(fn: Callable[[int, int], int], *args: int) -> int:
return fn(args[0], args[1])
Callable 定义为 [[参数类型, ...], 返回值类型]
NoReturn 无返回的函数
返回值类型,提示这个函数永远不会返回
from typing import NoReturn
def stop() -> NoReturn:
raise RuntimeError('no way')
无法重载继承final
坑
1、使用类作为类型提示报错未可用
某些情况下一些类型还没定义或在函数定义时导入该类型会导致循环导入,此时可以用字符串代替类型。 比如我把 Cat 定义放到 cat_factory 后面,此时 pyright 会提示 Cat unbound
解决方法1:
以字符串形式添加类型声明即可解决该问题
def cat_factory(name: str) -> 'Cat':
""" create a cat !"""
return Cat(name)
解决方法2:
某一行末尾使用 # type: ignore
注释提示 pyright
忽略此行类型检查。