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

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

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

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

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

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

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

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

二、基本类型注解

2.1 内置类型的注解

# 基本类型defgreet(name:str)->str:returnf"Hello,{name}!"defadd(a:int,b:int)->int:returna+bdefis_adult(age:int)->bool:returnage>=18defcalculate_average(scores:list[float])->float:"""Python 3.9+可以用list[float]"""returnsum(scores)/len(scores)ifscoreselse0.0# 复杂参数defprocess_data(data:list[int],# 列表,Python 3.9+写法config:dict[str,str],# 字典,Python 3.9+写法factor:float=1.0,)->list[float]:# 返回浮点数列表return[d*factorfordindata]# 使用result=process_data([1,2,3],{"mode":"fast"},2.0)print(result)# [2.0, 4.0, 6.0]

2.2 typing模块的常用类型

fromtypingimport(List,Dict,Tuple,Set,Optional,Union,Any,Callable,Sequence,Mapping,Iterable,Iterator)# Python 3.8及以下用typing模块的版本defprocess_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 —— 可以是某类型或Nonedefget_user(user_id:int)->Optional[dict]:"""返回用户字典或None"""users={1:{"name":"张三"}}returnusers.get(user_id)user=get_user(1)print(user)# {'name': '张三'}user2=get_user(99)print(user2)# None# Union —— 可以是多种类型之一defparse_value(value:str)->Union[int,float,str]:"""尝试解析值,返回不同可能的类型"""try:returnint(value)exceptValueError:try:returnfloat(value)exceptValueError:returnvalueprint(parse_value("42"))# 42 (int)print(parse_value("3.14"))# 3.14 (float)print(parse_value("hello"))# hello (str)# Any —— 任意类型defdebug_print(obj:Any)->None:"""接受任何类型的参数"""print(f"DEBUG:{obj!r}")# Callable —— 函数类型defapply_transform(data:list[int],transform:Callable[[int],int]# 接收int返回int的函数)->list[int]:return[transform(x)forxindata]result=apply_transform([1,2,3],lambdax:x*2)print(result)# [2, 4, 6]

三、高级类型注解

3.1 类型别名和自定义类型

fromtypingimportTypeAlias# 类型别名UserId:TypeAlias=intUserName:TypeAlias=strJsonDict:TypeAlias=dict[str,any]defget_user_name(user_id:UserId)->UserName:returnf"用户{user_id}"# 复杂的嵌套类型Matrix=list[list[float]]# 矩阵defmatrix_multiply(a:Matrix,b:Matrix)->Matrix:"""矩阵乘法"""pass# 使用Literal限制取值fromtypingimportLiteraldefset_log_level(level:Literal["DEBUG","INFO","WARNING","ERROR"])->None:"""level只能是这四个值之一"""print(f"日志级别设置为:{level}")set_log_level("INFO")# ✅# set_log_level("TRACE") # mypy会报警告(运行时不会报错)# 使用TypedDict定义字典结构fromtypingimportTypedDictclassUserDict(TypedDict):"""定义用户字典的结构"""name:strage:intemail:stractive:booldefcreate_user(data:UserDict)->str:returnf"用户{data['name']}已创建"# Final —— 常量,不应被修改fromtypingimportFinal MAX_RETRIES:Final=3API_BASE_URL:Final="https://api.example.com"

3.2 类型注解在类中的应用

fromtypingimportClassVar,SelfclassBankAccount:# ClassVar —— 类变量(所有实例共享)bank_name:ClassVar[str]="第一银行"total_accounts:ClassVar[int]=0def__init__(self,owner:str,balance:float=0.0)->None:self.owner:str=owner# 实例属性self.balance:float=balance BankAccount.total_accounts+=1defdeposit(self,amount:float)->float:"""存款——返回新的余额"""self.balance+=amountreturnself.balancedeftransfer_to(self,other:Self,amount:float)->bool:"""转账到另一个账户——Self表示同类型的实例"""ifself.balance>=amount:self.balance-=amount other.balance+=amountreturnTruereturnFalse# 使用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 查看注解信息

defprocess(data:list[int],factor:float=1.5)->dict[str,float]:"""处理数据"""result=sum(data)*factorreturn{"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$ pipinstallmypy# 检查Python文件$ mypy script.py# 输出示例:# script.py:10: error: Argument 1 to "process" has incompatible type "list[str]";# expected "list[int]"
# mypy通过类型注解和代码逻辑推断类型错误# 它不运行你的代码,而是静态分析# 即使你的代码运行正常,mypy也能发现潜在问题:defget_config(key:str)->Optional[dict]:config={"debug":{"enabled":True}}returnconfig.get(key)config=get_config("debug")# config可能是None——使用前需要检查# config["enabled"] # mypy警告: Item "None" of "Optional[dict]" has no attribute "__getitem__"# ✅ 正确处理Optionalconfig=get_config("debug")ifconfigisnotNone:print(config["enabled"])# mypy放心了——你检查过了

五、总结

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

💡核心要点:

  1. 类型注解不影响运行时——它们是元数据,供工具使用
  2. Python 3.9+用list[int],旧版本用typing.List[int]
  3. IDE支持——代码补全、错误提示、重构辅助
  4. mypy——在CI中加mypy检查,防止类型错误上线

渐进式采用策略:不用一次性给所有代码加类型。从公共API开始,逐步扩展。AnyOptional是你的过渡工具。