资讯详情

Python核心语法(五):类型注解

📅 2026/10/10 12:18:38 | 华诺云谱 👁 阅读
Python核心语法(五):类型注解
Python核心语法五类型注解前言前五篇我们梳理了数据存储与运算、流程控制语句、数据容器、函数、正则表达式程序已经能存数据、做判断、管理集合、封装代码、处理文本了。但你有没有遇到这种情况写函数的时候参数名叫 data写的时候记得它是字符串过两周再打开——诶这是字符串还是列表来着只能翻函数内部一行行看。还有同事调用你写的函数把整数传给了只该收字符串的参数程序跑起来没报错结果输出一堆乱码查了半天。这一篇整理类型注解——给变量、参数、返回值贴标签的语法。它不改变代码的运行方式却能让代码自己说话一眼看清每个数据是什么类型。而且这是当下 Python 圈的大势所趋FastAPI、Pydantic 这些热门框架靠它实现自动校验AI 编程助手Copilot 等靠它猜得更准大厂开源项目几乎全部在用。类型注解主要分为四大块变量与容器的类型注解给普通变量、列表、字典、集合贴标签函数的类型注解参数类型、返回值类型把函数说明书写进代码typing 模块与常用类型Optional、Any、Union、Callable……应对更复杂的场景静态检查工具让贴标签从摆设变成真正能查错的利器一、类型注解是什么1.生活中的类比你收到一个快递箱上面只写着东西你会发懵这是书是杯子还是易碎的玻璃但如果贴着标签——“内容物玻璃杯易碎向上”——你立刻知道怎么对待它。类型注解就是贴在变量上的快递标签。类型注解Type Annotation也叫类型提示 Type Hint写在代码里、用来说明这个数据是什么类型的标注语法。它只负责声明不负责拦截——就像快递标签能提醒搬运工小心但没法阻止搬运工暴力抛掷。2.最基本的语法变量注解在变量名后面加冒号写上类型。name:str张三age:int20height:float1.75is_vip:boolTrue函数注解参数类型写在冒号后面返回值类型写在-后面。defadd(a:int,b:int)-int:returnab读作add 函数收两个整数返回一个整数。是不是有点像数学里的函数定义 f(x: 整数) → 整数*注意类型注解不改变运行结果。写 name: str “张三” 和 name “张三” 跑起来一模一样注解只是给人看和给工具看的说明书。二、变量与容器的类型注解1.普通变量name:str张三age:int20score:float88.5is_pass:boolTrue输出结果没有输出这只是声明赋值注解不产生任何输出*注意变量注解可以只声明不赋值x: int 这一行完全合法此时 x 还不存在没绑定值但类型已经登记了。适合先声明后面再赋值的场景。2.容器注解要写内容物类型只写 list 太笼统——列表里装的是整数还是字符串要用方括号写清楚内容物# 列表装 int 的列表scores:list[int][90,85,78]# 元组装 str 的元组cities:tuple[str,...](北京,上海,深圳)# 字典键是 str值是 int两种类型用逗号隔开id_map:dict[str,int]{张三:1,李四:2}# 集合装 float 的集合temps:set[float]{36.5,37.0}输出结果同样没有输出纯注解声明*注意元组的特殊写法固定长度的元组写 tuple[int, str]第一个是 int、第二个是 str长度不定的写 tuple[int, …]三个点表示后面全是 int数量不限。字典 dict[键类型, 值类型] 两个都要写只写一个报 TypeError。3.3.9 之前的老写法看懂旧代码Python 3.9 之前list[int] 这种写法会报错必须从 typing 模块导入大写开头的类型fromtypingimportList,Dict,Tuple,Set scores:List[int][90,85,78]id_map:Dict[str,int]{张三:1}现在写代码一律用小写的 list[int]、dict[str, int] 就好但读别人尤其网上的旧代码、旧教程时要认得 List、Dict 这些大写形式是一个意思。三、函数的类型注解1.给参数和返回值贴标签defadd(a:int,b:int)-int:returnabdefgreet(name:str)-str:return你好namedeflog_message(msg:str)-None:print(f[日志]{msg})print(add(3,5))print(greet(张三))log_message(服务启动)输出结果8 你好张三 [日志] 服务启动拆解一下 greet(“张三”) 的注解name: str → 参数 name 是字符串- str → 返回值是字符串*注意没有返回值的函数返回值类型写 - None。像 log_message 这种只打印不 return 的函数写 - None也可以省略不写但规范做法是写上。2.注解错了也不报错重点defdouble(n:int)-int:returnn*2resultdouble(hi)print(result)输出结果hihi明明注解说参数是 int传了个字符串 “hi”程序不但不报错还正常运行输出 “hihi”字符串乘法重复两遍*注意这是类型注解最颠覆新手认知的一点Python 运行时不会检查类型注解注解错了照样跑直到哪天逻辑炸了才暴露。它是说明书不是安检门。那注解的意义在哪一是给人看IDE 会提示、报错警告二是给工具看mypy 等静态检查工具见第六章。3.默认值与注解同时存在写法顺序是参数名: 类型 默认值冒号在前、等号在后defintroduce(name:str,age:int18)-str:returnf我叫{name}今年{age}岁print(introduce(张三))print(introduce(李四,22))输出结果我叫张三今年18岁 我叫李四今年22岁*注意默认值是 None 时类型要写 str | None或 Optional[str]见第四章不能写 str None——注解说必须是字符串却给个 None工具检查时算自相矛盾。# 错误示范mypy 会警告deffind(name:strNone)-str:...# 正确写法deffind(name:str|NoneNone)-str|None:...4.可变类型默认值的大坑第四章讲过默认值用可变对象列表、字典会被所有调用共享。类型注解也一样defappend_item(item:str,my_list:list[str][])-list[str]:my_list.append(item)returnmy_listprint(append_item(苹果))print(append_item(香蕉))输出结果[苹果] [苹果, 香蕉]第二次调用把苹果也带上了——因为默认的 [] 是同一个列表一直在被追加。正确做法是用 None 占位defappend_item(item:str,my_list:list[str]|NoneNone)-list[str]:ifmy_listisNone:my_list[]my_list.append(item)returnmy_listprint(append_item(苹果))print(append_item(香蕉))输出结果[苹果] [香蕉]四、typing 模块与常用类型简单类型用 int、str 就够了但真实业务远比这复杂参数可能是字符串也可能是 None、可能是多种类型之一、可能是另一个函数……这些靠 typing 模块和 Python 3.10 后的新语法解决。1.Optional可能有也可能没有Optional[X]表示X 类型或者 None专治查不到就返回 None的场景。fromtypingimportOptionaldeffind_user(user_id:int)-Optional[str]:users{1:张三,2:李四}returnusers.get(user_id)# get 查不到时返回 Noneprint(find_user(1))print(find_user(99))输出结果张三 NonePython 3.10 的新写法是直接用竖线deffind_user(user_id:int)-str|None:users{1:张三,2:李四}returnusers.get(user_id)*注意Optional[str] 和 str | None 是完全等价的两种写法新版直接用竖线更简洁。竖线是 Python 3.10 才支持的语法如果项目要兼容更老的版本就得用 Optional。2.Union多选一Union[X, Y]表示X 或 Y 都行适合既能传单个值又能传列表这类灵活参数。fromtypingimportUniondefshow_id(user_id:Union[int,str])-None:print(f用户ID{user_id})show_id(1001)show_id(A1001)输出结果用户ID1001 用户IDA1001新写法Union[int, str]等价于int | str竖线可以连着写多个int | str | None。*注意Union[X, Y] 里写了 None 等价于 X | Y | None所以 Union[str, None] 就是 Optional[str]俩工具可以互相替换别被不同写法迷惑。3.Any万金油Any表示任意类型都行相当于没有注解。适合暂时不想约束、或者写得太杂懒得列全的场景。fromtypingimportAnydefprint_anything(data:Any)-None:print(f收到{data})print_anything(123)print_anything(文本)print_anything([1,2,3])输出结果收到123 收到文本 收到[1, 2, 3]*注意Any 是逃生舱口而不是免死金牌。用 Any 的地方工具就不会帮你检查了等于放弃保护。能写具体类型就写具体类型Any 只在确实无法确定时使用。4.函数也能当参数函数名本身就是变量当然也有类型。用 Callable 注解参数是一个函数fromtypingimportCallabledefapply_twice(func:Callable[[int],int],value:int)-int:returnfunc(func(value))defadd_one(n:int)-int:returnn1print(apply_twice(add_one,5))输出结果7*注意Callable[[int], int] 的两个方括号含义里面第一个 [int] 是参数类型列表多个参数逗号隔开外面那个 int 是返回值类型。读作接收一个 int 参数、返回 int 的函数。五、给自定义类做注解类型注解不只是内置类型的专利自己写的类同样可以也是框架里最常见的用法classStudent:def__init__(self,name:str,age:int)-None:self.namename self.ageagedefprint_student(stu:Student)-None:print(f姓名{stu.name}年龄{stu.age})sStudent(张三,20)print_student(s)输出结果姓名张三年龄20*注意引用还没定义的类比如 A 类的方法返回 A 自己或两个类互相引用时类型名要加引号写成 “A”这叫前向引用。Python 3.14 起默认自动处理这种场景不用加引号但看旧代码时要认得而且 IDE 里的 Pylance、mypy 对没加引号的情况也可能提前告警。六、静态检查工具让标签真正管用写到这儿你可能想既然注解运行时不检查那不就是个好看的注释吗——单独看确实是。但配合静态检查工具它就从说明书升级成安检门。1.mypy最主流的类型检查工具mypy 的逻辑是不运行代码光读代码拿工具眼里实际类型和注解声明类型做对比对不上就报错。安装pip install mypy准备一个故意出错的文件 demo.pydefdouble(n:int)-int:returnn*2resultdouble(hi)print(result)在命令行运行检查不运行代码mypy demo.py输出结果demo.py:4: error: Argument 1 to double has incompatible type str; expected int [arg-type] Found 1 error in 1 file (checked 1 source file)第 4 行直接被揪出来你声明参数是 int却传了个 str。同一个文件直接用 python 运行会输出 hihi 什么都不报mypy 一查一个准。*注意mypy 报错的文件名和行号可能因运行环境略有差异比如显示相对路径、多一个方括号错误码属正常现象关注第几行、什么错即可。2.IDE 提示写代码时就受益其实不装 mypy你平时用的编辑器VS Code Pylance、PyCharm读了类型注解后就会调用函数时弹参数提示鼠标悬停 add 函数显示 (a: int, b: int) - int传错类型时画波浪线警告打点号时智能补全注解越准补全越准这也是为什么说 AI 编程助手依赖类型注解——提示越明确AI 猜得越准生成的代码质量越高。3.实践的度别走火入魔*注意类型注解是渐进式的Python 官方的设计哲学就是不强制。可以从只给函数签名加注解开始收益最大、成本最小变量类型一眼能看出来的name “张三”就不用注解。不要为了注解把代码写得密密麻麻反而伤害可读性。七、易混淆知识点对比1.类型注解 vs 运行时强制检查最高频误区对比项类型注解作用声明应该是什么类型运行时是否检查否传错类型照样跑谁来检查IDE 警告、mypy 等静态工具2.list[int] vs typing.List[int]对比项list[int]List[int]可用版本Python 3.9Python 3.5现在仍可用属旧写法写法来源内置类型直接用从 typing 模块导入现在推荐是仅用于看懂旧代码3.Optional[str] vs str | None对比项Optional[str]str | None可用版本Python 3.5Python 3.10等价性完全等价完全等价新代码推荐看团队规范是更简洁4.注解写在哪场景写法变量name: str “张三”函数参数def f(a: int)函数返回值def f(a: int) - str默认值参数def f(a: int 0)默认为空def f(a: int | None None)5.Any vs 不写注解对比项Any不写态度明确声明任意类型没声明工具态度检查时跳过该处按规则推断语义“我故意的”“我忘了/懒得写”八、小结类型注解是快递标签变量名: 类型、参数: 类型、- 返回值类型只声明、不拦截运行时不改变任何行为。变量注解name: str “张三”容器要写内容物list[int]、dict[str, int]、tuple[str, …]、set[float]。函数注解def add(a: int, b: int) - int无返回值写 - None默认值写法是 参数名: 类型 默认值。Optional[str] str | None可能为空Union[int, str] int | str多选一Any 是放弃检查的万金油Callable[[int], int] 表示收 int 返回 int 的函数。可变默认值大坑list、dict 做默认值会被共享用 None 占位 函数内新建。运行时不检查≠没用mypy 静态检查能把声明与实际对不上的地方揪出来IDE 靠注解给提示和补全AI 编程助手靠它猜得更准。渐进式使用先给函数签名加注解收益最大变量一眼能看懂的不用注解老代码里 typing.List 是 list 的旧写法。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑