Python 更好的使用类型提示

利用typing做到更好的类型提示

mypy

mypy是一个可以帮助我们检查类型的第三方库,通过 pip insatll mypy 安装,然后执行 mypy <py文件> 即可自动帮我们检测数据类型是否使用正确.例如我们有这样一段代码。

from typing import List


my_type = List[int]

test: my_type = [1, 2, 3]

我们期望创建一个列表,列表内部的数据是int类型,当我们按照规矩创建 test 之后利用 mypy 检查,可以得到结果

但是假如我们并不按规矩创建 test 呢?例如我们创建 testtest: my_type = [1, 2, "three"],此时再利用 mypy 检查会得到结果

是的,mypy会明确指出不合预期的数据类型,并告诉我们发生在那里

List的使用

当我们需要列表的内部是int类型时我们可以这样操作

from typing import List


def test_fun(value: List[int]):
    return sum(value)

# test_fun([1, 2, 3])

Tuple

当需要传入一个元祖时我们可以使用 Tuple

from typing import Tuple


test: Tuple[str, int, float] = ("one", 2, 3.0)

可以发现TupleList略有不同, Tuple 需要指定每一个位置上元素的类型。假如我们并不关注某个元素的数据类型时,我们可以直接使用内置类型 tuple

test: tuple = ("one", 2, 3.0, [4, 5, 6])

Dict

当我们需要字典的内部键值对类型时我们可以这样操作

from typing import Dict


my_type = Dict[str, int]

# test: my_type = {"one": 1, "two": 2, "three": 3}

注意此段代码第2行中,str指定的是字典中每一个键的类型;int指定的是字典中每一个值的类型。

TypedDict

上面我们用 Dict 做到了类型提示,但假如我们不满足于此,我们想为每一个键设置不同类型的值的提示要怎么办呢?利用 TypeDict 可以解决这个问题。

from typing import TypedDict, List


class MyType(TypedDict):
    one: str
    two: List[int]


# test: MyType = {"one": "str", "two": [1, 2]}

Union

当我们输入的参数类型有多种可选项时可以使用 Union

from typing import Union


def test(value: Union[str, int]):
    return value


# test(value="1")
# test(value=2)

需要注意的是从Python3.10开始 Union 被替换为 | 意味着 Union[X, Y] 等价于 X | Y

def test(value: str | int):
    return value


# test(value="1")
# test(value=2)

Callable

当你需要使用一个函数作为参数时,这个参数的类型提示可以使用 Callable

from typing import Callable


def test_1(x: int, y: int) -> int:
    return x + y


def test_2(x: int, y: int, fun: Callable) -> int:
    output = fun(x, y)
    return output

# test_2(1, 2, test_1)

同时我们可以利用 Callable 指定被传入函数的参数的类型提示,规则为 [[传入参数1的类型, 传入参数2的类型...], 返回数据的类型]

from typing import Callable


def test_1(x: int, y: int) -> int:
    return x + y


def test_2(x: int, y: int, fun: Callable[[int, int], int]) -> int:
    output = fun(x, y)
    return output

# test_2(1, 2, test_1)

Any

当传入的参数可以为任何类型的时候可以使用 Any

from typing import Any


def test(value: Any):
    return value

Optional

当你的函数参数有默认值,导致参数不是必须要传入的参数,那么你可以尝试使用 Optional 来做到类型提示

from typing import Optional


def test(value: Optional[int] = None):
    return value

# test()

从上面的代码我们发现, Optional 作用几乎和带默认值的参数等价。但其实是有些许区别的,假设我们声明参数为 value: int = None 用静态检查工具去检查代码时会返回错误给我们。而我们为 value 指定数据类型为 Optional[int] 但默认值为 None 经过静态检查工具也不会返回错误。所以其实Optional[X] 是等价于 Union[X, None] 的。

Sequence

Sequence 所提示的是任何可以被索引的数据: 列表,元祖,字符串等

from typing import Sequence


def test(value: Sequence[str]):
    print(value)

# test(["1", "2", "3"])
# test(("1", "2", "3"))
# test("1233")

注意在定义函数时 [] 中只可以指定一个数据类型,意味着在我们传入的列表或者元祖等可索引数据时,内部元素的数据类型需要是统一的。