Python 之类型注解

类型注解允许开发者显式地声明变量、函数参数和返回值的类型。但是加不加注解对于程序的运行没任何影响(是非强制的,且类型注解不影响运行时行为),属于 有了挺好,没有也行。但是大型项目按照规范添加注解的话,对于后期开发和维护是很有帮助的,毕竟不用回退好几层去推断有些变量的类型。

与原生数据类型的区别

特性类型注解原生数据类型
本质仅为代码中的类型提示信息,不影响代码运行时的行为实际存储和操作数据的结构,决定了数据的操作方式和内存占用
作用提高代码可读性,辅助静态类型检查用于实际的数据存储和处理
定义方式在变量名后使用冒号和类型名称进行标注通过赋值语句创建具体的数据对象

为什么用注解

代码补全

PyCharm 能根据类型注解提供更准确的属性/方法建议(如知道 y: str 后,输入 y. 会提示 str 的方法)。

比如我读取一个 json 文件并 json.load 后进行处理,像下面这种,在调用 data 的 items() 方法时,PyCharm 是没有方法提示的(毕竟 PyCharm 没办法未卜先知,无法提前预测加载后的 data 是什么类型,这也能理解)。

import json


if __name__ == '__main__':
    data = json.load(open("data.json", encoding="utf-8"))
    for key1, value1 in data.items():
        for key2, value2 in value1.items():
            print(f"{key1}\t{key2}\t{value2}")

添加注解以后,代码补全提示就方便多了。 

import json
from typing import Dict


if __name__ == '__main__':
    data: Dict[str, Dict[str, str]]  # 提前添加对 data 的注解
    data = json.load(open("data.json", encoding="utf-8"))
    for key1, value1 in data.items():
        for key2, value2 in value1.items():
            print(f"{key1}\t{key2}\t{value2}")

类型提示

添加类型注解以后,如果赋值的数据类型和注解声明的类型不一致的话,PyCharm 会进行提示,能够一眼洞察类型不符的情况,提前发现错误,避免运行时因类型错误导致的 TypeError 报错

维护方便

在多人协作或长期项目中,类型注解降低了理解代码的门槛,减少因类型混淆导致的 Bug,对于后期维护很有帮助。 

可读性提高

类型注解明确声明了参数和返回值的预期类型,使函数接口的语义一目了然。例如 def get_user(id: int) -> User 比未注解的版本更清晰,减少对参数类型的文字描述。

基础类型注解

Python 内置的基本类型可以直接用于注解。

# 变量注解
name: str = "Alice"
age: int = 30
price: float = 19.99
is_active: bool = True


# 函数参数和返回值注解
def greet(name: str) -> str:
    return f"Hello, {name}"


if __name__ == '__main__':
    name = "Looking"
    print(type(name))  # <class 'str'>
    greet_word = greet(name)
    print(greet_word)  # Hello, Looking

复合类型注解

from typing import List, Dict, Tuple, Set, Optional

# 列表
numbers: List[int] = [1, 2, 3]

# 字典
person: Dict[str, str] = {"name": "Alice", "email": "alice@example.com"}

# 元组 (固定长度和类型)
point: Tuple[float, float] = (3.14, 2.71)

# 集合
unique_numbers: Set[int] = {1, 2, 3}

# 可选类型 (表示 middle_name 有值的时候是 str,无值的时候可以是 None)
middle_name: Optional[str] = None

函数类型注解

from typing import Callable


# 基本函数注解
def add(a: int, b: int) -> int:
    return a + b


# 带默认值的参数
def greet(name: str, greeting: str = "Hello") -> str:
    return f"{greeting}, {name}"


# 函数作为参数
def apply_func(func: Callable[[int, int], int], x: int, y: int) -> int:
    return func(x, y)


if __name__ == '__main__':
    v1 = add(1, 2)
    print(v1)  # 3
    v2 = greet("Looking")
    print(v2)  # Hello, Looking
    v3 = apply_func(add, 2, 3)
    print(v3)  # 5

特殊类型注解

from typing import Any, Union, NoReturn, NewType


# Any - 任意类型
def log(message: Any) -> None:
    print(message)


# Union - 多个可能的类型
def square(number: Union[int, float]) -> Union[int, float]:
    return number ** 2


# NoReturn - 函数不会正常返回
def fail() -> NoReturn:
    raise Exception("Something went wrong")


UserId = NewType("UserId", int)
if __name__ == '__main__':
    log(123)  # 123
    log("hello world")  # hello world
    print(square(5))  # 25
    print(square(2.5))  # 6.25
    # UserId("hello")  # 类型检查不通过
    print(UserId(12345))  # 12345
    fail()
    # Traceback (most recent call last):
    #   File "E:\lky_project\tmp_project\test.py", line 24, in <module>
    #     fail()
    #   File "E:\lky_project\tmp_project\test.py", line 16, in fail
    #     raise Exception("Something went wrong")
    # Exception: Something went wrong

类型注解别名

from typing import List, Tuple

# 简单别名
UserId = int

# 复杂别名
Point = Tuple[float, float]
Vector = List[float]


def distance(point1: Point, point2: Point) -> float:
    return ((point1[0] - point2[0]) ** 2 + (point1[1] - point2[1]) ** 2) ** 0.5


def normalize(vector: Vector) -> Vector:
    length = sum(x ** 2 for x in vector) ** 0.5
    return [x / length for x in vector]


if __name__ == '__main__':
    point1 = (3, 4)
    point2 = (0, 0)
    print(distance(point1, point2))  # 5.0

    vector = [3, 4]
    print(normalize(vector))  # [0.6, 0.8]

泛型类型注解

from typing import TypeVar, Generic, List

T = TypeVar('T')  # 声明无约束的类型变量


class Stack(Generic[T]):
    def __init__(self) -> None:
        self.items: List[T] = []

    def push(self, item: T) -> None:
        self.items.append(item)

    def pop(self) -> T:
        return self.items.pop()


if __name__ == '__main__':
    # 使用
    int_stack = Stack[int]()  # 初始化实例并指定 T 的类型
    int_stack.push(12345)
    print(int_stack.items)  # [12345]

    str_stack = Stack[str]()
    str_stack.push("hello")
    print(str_stack.items)  # ['hello']

类型变量

from typing import TypeVar

# 无约束的类型变量(表明 T 可以是无约束的任何类型)
T = TypeVar('T')

# 有约束的类型变量
Number = TypeVar('Number', int, float, complex)


def double(x: Number) -> Number:
    return x * 2


def triple(x: T) -> T:
    return x * 3


if __name__ == '__main__':
    print(double(123))  # 246
    print(triple("hello "))  # hello hello hello
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值