Skip to main content

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

img

解决方法1:

以字符串形式添加类型声明即可解决该问题

def cat_factory(name: str) -> 'Cat':
""" create a cat !"""
return Cat(name)

解决方法2:

某一行末尾使用 # type: ignore 注释提示 pyright 忽略此行类型检查。