类型注解

Python 是一门动态语言,因此我们不需要像 c 那样显式的声明变量类型,Python 程序在运行时会隐式的推断出变量的类型。这样的好处是我们可以给一个变量赋不同的变量类型,非常的灵活,这个特性刚用 Python 的时候确实很爽,用多了以后会发现这种特性会带来很多出其不意的 bug,而编译器不会在编译阶段告诉我们,我们只有运行的时候自己去发现。而静态语言由于变量在整个生命周期只有一个类型,对我们编程要求更为严格,因此代码通常会更健壮。

顺便提下强类型和弱类型的区别 [^1]。

类型系统的“强/弱”指的是当编程语言遇到与类型定义不匹配的运算时,尝试猜测或者转换的力度/程序。它不是一条明确的界限,而是一个范围。

  • 强类型语言在遇到与类型定义明显矛盾的运算时,一般会当做一种语法错误,而不会尝试对值的类型进行转换。
  • 弱类型语言恰好相反,会猜测程序员的意图,并对其中一些值的类型进行转换,以让程序继续执行。

由于这种难 debug 的特性,因此 Python 在 3.5 以后引入了类型注解(同样的操作发生在了 javascript 和 typescript 之间),如果使用 vscode,那么结合 Pylance 插件,会有很好的编码体验。

变量类型的注解:Python 中基本的变量类型,类型注解都是支持的,比如:

n: int = 3
pi: float = 3.14
s: str = 'abc'
flag: bool = False

上面的类型比较简单,那么看复杂一点的,比如 dict, turple, list比如Dict, Turple, List,这些类型需要用 [] 来指定内部的类型。首先需要从 typing 模块中导入这些类型(Python3.9后已弃用)

l: list[int] = [n]
t: tuple[float, float] = (pi, pi)
d: dict[str, bool] = {s: flag}

此外,还可以使用一种特殊的类型 Any,他和所有类型互相兼容

from typing import Any
Vector = list[Any]
def scale(scalar: int, vector: Vector) -> Vector:
    return [scalar * num for num in vector]
new_vector = scale(2, ["a", "b", "c"])

同样的,我们也可以注解自定义的类型

class MyType:
    def __init__(self, n: int):
        self.n = n

n: int = 1
mytype = MyType(n)

需要注意的是类型检查只是做个检查,起个提示的作用,不会对运行产生影响,即使有错误的赋值,但是只要不引发异常仍然能运行。比如 l: list[int] = [pi],pylance 会提示我们这里的类型问题,但是这行代码仍然是能跑的。

函数的类型注解:函数的类型注解更常用,因此也更重要。函数中的参数有类型,函数可能有返回值,那么也有类型,也可能没有返回值,还有默认参数。总结如下:

# 带返回值、默认参数的函数
def func(n: int, s: str = 'default string') -> List[int]:
    print(s)
    return [n]
# 返回值为空的函数
def no_return():
    print('no return1')
# 或
def no_return() -> None:
    print('no return2')

需要注意的是,上述的 no_return 函数其实不是严格意义上的不返回值,他有返回值,但是为 None

类型别名:我们也可以给类型指定一个有特殊含义的别名。用等号就能直接取别名。(Python3.12 后可以使用 type 声明类型别名)。

Vector = list[float]
def scale(scalar: float, vector: Vector) -> Vector:
    return [scalar * num for num in vector]

类型的更多用法:包括泛型、特殊类型等 [^2]。

如果要指定变量的类型有多种可能,可以使用 Union(Python3.10 后可以使用 |代替),比如我们指定列表元素只能为 floatint

Vector = list[Union[float, int]]
# Python3.10 后等价于
Vector = list[float | int]

如果要使用“枚举”类型的变量,可以用 Literal 来向类型检查器说明被注解的对象具有与所提供的字面量之一相同的值。比如:

Mode = Literal['r', 'rb', 'w', 'wb']
def open_file(file: str, mode: Mode) -> str:
    ...

注释

docstring

Python 的文档字符串用于注释代码. 文档字符串是位于包、模块、类或函数里第一个语句的字符串. 可以用对象的 doc 成员自动提取这些字符串, 并为 pydoc 所用. (可以试试在你的模块上运行 pydoc 并观察结果). 文档字符串一定要用三重双引号 “”" 的格式 (依据 PEP-257 ). 文档字符串应该是一行概述 (整行不超过 80 个字符), 以句号、问号或感叹号结尾. 如果要写更多注释 (推荐), 那么概述后面必须紧接着一个空行, 然后是剩下的内容, 缩进与文档字符串的第一行的第一个引号对齐.[^3]

todo

python 中可以用 TODO (FIXME 等)开头的注释来表示待办等提示,配合 Todo Tree 使用效果不错,注释应该写成

我的插件配置参考了 [^4]

# TODO

动态语言、静态语言、强类型语言、弱类型语言的区别

References

[^1]:动态语言、静态语言、强类型语言、弱类型语言的区别
[^2]:typing-对类型提示的支持-Python 3.12.0 文档
[^3]:Python风格规范-Google开源项目风格指南
[^4]:VScode-TodoTree待办事项插件的定制和使用-Librarookie-博客园