云计算百科
云计算领域专业知识百科平台

Python函数:函数注解与类型提示的写法与实践

Python函数:函数注解与类型提示的写法与实践

在这里插入图片描述

一、开篇:让代码"声明意图"

Python是动态类型语言——你不需要声明变量的类型。这在快速开发时很方便,但在大型项目中,缺乏类型信息常常导致难以发现的bug。

⌨️ 从Python 3.5开始,函数注解(Function Annotations)和类型提示(Type Hints)成为了标准:

# 没有类型提示的函数
def calculate_total(items, discount, tax_rate):
"""这些参数应该传什么类型?返回值是什么类型?"""
pass

# ✅ 有类型提示的函数——意图清晰
def calculate_total(
items: list[dict], # 应该是字典列表
discount: float, # 折扣率(小数)
tax_rate: float = 0.13 # 税率,默认13%
) > float: # 返回浮点数
"""现在一看就知道该怎么传参了"""
pass

💡 类型提示不会影响运行时行为——Python不会因为类型不匹配而报错。它们的主要作用是:提升代码可读性、获得更好的IDE支持(自动补全、错误提示)、以及配合静态类型检查工具(如mypy)在运行前发现问题。

二、基本类型注解

2.1 内置类型的注解

# 基本类型
def greet(name: str) > str:
return f"Hello, {name}!"

def add(a: int, b: int) > int:
return a + b

def is_adult(age: int) > bool:
return age >= 18

def calculate_average(scores: list[float]) > float:
"""Python 3.9+可以用list[float]"""
return sum(scores) / len(scores) if scores else 0.0

# 复杂参数
def process_data(
data: list[int], # 列表,Python 3.9+写法
config: dict[str, str], # 字典,Python 3.9+写法
factor: float = 1.0,
) > list[float]: # 返回浮点数列表
return [d * factor for d in data]

# 使用
result = process_data([1, 2, 3], {"mode": "fast"}, 2.0)
print(result) # [2.0, 4.0, 6.0]

2.2 typing模块的常用类型

from typing import (
List, Dict, Tuple, Set, Optional, Union, Any, Callable,
Sequence, Mapping, Iterable, Iterator
)

# Python 3.8及以下用typing模块的版本
def process_old(
data: List[int], # 等价于 Python 3.9+ 的 list[int]
config: Dict[str, str], # 等价于 Python 3.9+ 的 dict[str, str]
sizes: Tuple[int, int], # 等价于 Python 3.9+ 的 tuple[int, int]
tags: Set[str], # 等价于 Python 3.9+ 的 set[str]
) > List[float]:
pass

# Optional —— 可以是某类型或None
def get_user(user_id: int) > Optional[dict]:
"""返回用户字典或None"""
users = {1: {"name": "张三"}}
return users.get(user_id)

user = get_user(1)
print(user) # {'name': '张三'}
user2 = get_user(99)
print(user2) # None

# Union —— 可以是多种类型之一
def parse_value(value: str) > Union[int, float, str]:
"""尝试解析值,返回不同可能的类型"""
try:
return int(value)
except ValueError:
try:
return float(value)
except ValueError:
return value

print(parse_value("42")) # 42 (int)
print(parse_value("3.14")) # 3.14 (float)
print(parse_value("hello")) # hello (str)

# Any —— 任意类型
def debug_print(obj: Any) > None:
"""接受任何类型的参数"""
print(f"DEBUG: {obj!r}")

# Callable —— 函数类型
def apply_transform(
data: list[int],
transform: Callable[[int], int] # 接收int返回int的函数
) > list[int]:
return [transform(x) for x in data]

result = apply_transform([1, 2, 3], lambda x: x * 2)
print(result) # [2, 4, 6]

三、高级类型注解

3.1 类型别名和自定义类型

from typing import TypeAlias

# 类型别名
UserId: TypeAlias = int
UserName: TypeAlias = str
JsonDict: TypeAlias = dict[str, any]

def get_user_name(user_id: UserId) > UserName:
return f"用户{user_id}"

# 复杂的嵌套类型
Matrix = list[list[float]] # 矩阵

def matrix_multiply(a: Matrix, b: Matrix) > Matrix:
"""矩阵乘法"""
pass

# 使用Literal限制取值
from typing import Literal

def set_log_level(level: Literal["DEBUG", "INFO", "WARNING", "ERROR"]) > None:
"""level只能是这四个值之一"""
print(f"日志级别设置为: {level}")

set_log_level("INFO") # ✅
# set_log_level("TRACE") # mypy会报警告(运行时不会报错)

# 使用TypedDict定义字典结构
from typing import TypedDict

class UserDict(TypedDict):
"""定义用户字典的结构"""
name: str
age: int
email: str
active: bool

def create_user(data: UserDict) > str:
return f"用户{data['name']}已创建"

# Final —— 常量,不应被修改
from typing import Final
MAX_RETRIES: Final = 3
API_BASE_URL: Final = "https://api.example.com"

3.2 类型注解在类中的应用

from typing import ClassVar, Self

class BankAccount:
# ClassVar —— 类变量(所有实例共享)
bank_name: ClassVar[str] = "第一银行"
total_accounts: ClassVar[int] = 0

def __init__(self, owner: str, balance: float = 0.0) > None:
self.owner: str = owner # 实例属性
self.balance: float = balance
BankAccount.total_accounts += 1

def deposit(self, amount: float) > float:
"""存款——返回新的余额"""
self.balance += amount
return self.balance

def transfer_to(self, other: Self, amount: float) > bool:
"""转账到另一个账户——Self表示同类型的实例"""
if self.balance >= amount:
self.balance -= amount
other.balance += amount
return True
return False

# 使用
acc1 = BankAccount("张三", 1000.0)
acc2 = BankAccount("李四", 500.0)
acc1.transfer_to(acc2, 300.0)
print(f"张三: {acc1.balance}, 李四: {acc2.balance}")
# 张三: 700.0, 李四: 800.0

四、类型检查工具

4.1 查看注解信息

def process(data: list[int], factor: float = 1.5) > dict[str, float]:
"""处理数据"""
result = sum(data) * factor
return {"total": result, "count": len(data)}

# __annotations__属性存储了类型注解
print(process.__annotations__)
# {'data': list[int], 'factor': <class 'float'>, 'return': dict[str, float]}

# 类型注解只是元数据——不影响运行时
print(process([1, 2, 3], 2.0)) # {'total': 12.0, 'count': 3}
print(process(["a", "b"], "hello")) # 运行时不报错,但类型不对!
# mypy会在静态检查时报错

4.2 mypy使用简介

# 安装mypy
$ pip install mypy

# 检查Python文件
$ mypy script.py

# 输出示例:
# script.py:10: error: Argument 1 to "process" has incompatible type "list[str]";
# expected "list[int]"

# mypy通过类型注解和代码逻辑推断类型错误
# 它不运行你的代码,而是静态分析

# 即使你的代码运行正常,mypy也能发现潜在问题:
def get_config(key: str) > Optional[dict]:
config = {"debug": {"enabled": True}}
return config.get(key)

config = get_config("debug")
# config可能是None——使用前需要检查
# config["enabled"] # mypy警告: Item "None" of "Optional[dict]" has no attribute "__getitem__"

# ✅ 正确处理Optional
config = get_config("debug")
if config is not None:
print(config["enabled"]) # mypy放心了——你检查过了

五、总结

类型提示是现代Python的重要特性。它让你在享受动态类型灵活性的同时,获得静态类型的代码辅助和质量保证。

💡 核心要点:

  • 类型注解不影响运行时——它们是元数据,供工具使用
  • Python 3.9+用list[int],旧版本用typing.List[int]
  • IDE支持——代码补全、错误提示、重构辅助
  • mypy——在CI中加mypy检查,防止类型错误上线
  • ✅ 渐进式采用策略: 不用一次性给所有代码加类型。从公共API开始,逐步扩展。Any和Optional是你的过渡工具。

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » Python函数:函数注解与类型提示的写法与实践
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!