一、开篇:让代码"声明意图"
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是你的过渡工具。
到此这篇关于python 函数注解与类型提示的写法与实践的文章就介绍到这了,更多相关python 函数注解与类型提示内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论