📄 一句话概括
同一个 sum 换输入,结果类型自动跟着变,代码 0 处类型声明;但 Python 的注解既不检查也不推断——传字符串照样跑出 xy。
📝 本节和前面几节的关系
A.8 讲循环,这一节讲类型——功能式的第 7 条特征,也是 Python 最不给力的一条。
一、实测:类型跟着输入变
sum([1, 2, 3]) 得 6(int);sum([1.0, 2.0, 3.0]) 得 6.0(float);sum([True, True, False]) 得 2(int)。
同一个函数、同一份代码,类型由输入决定。
所以【自动推断】的好处不是省字,是省事。
二、Python 的注解是什么
只是记录:typed.__annotations__ 存下来,运行时不检查、不阻断。
也不推断:写 a + b 不会反推出 int。
实测 typed("x", "y") 返回 'xy'——注解写成 int,照样给你字符串。
三、真正的类型推断长什么样
编译期就把类型算出来:不写一个类型,编译器也知道每个表达式是什么。 好处:写起来像动态语言,出错却在编译期就拦下。 代价:推断不出来时要手写注解,报错信息也常常晦涩。
四、动手 Demo:特征7:编译器自动进行类型推断
同一个 sum 换三种输入,看结果类型。 再验 Python 的注解会不会被检查。
print(sum([1, 2, 3]), type(sum([1, 2, 3])).__name__)
print(sum([1.0, 2.0, 3.0]), type(sum([1.0, 2.0, 3.0])).__name__)
print(sum([True, True, False]))
def typed(a: int, b: int) -> int:
return a + b
print(typed.__annotations__)
print(typed("x", "y")) # xy —— 注解没拦住
实跑输出:
--------------------------------------------------------------------
特征7:编译器自动进行类型推断
--------------------------------------------------------------------
【自动推断】=不写类型,编译器自己算出来。**Python 是动态类型,只做到一半:**
sum([1, 2, 3]) 得 6(int)
sum([1.0, 2.0, 3.0]) 得 6.0(float)
sum([True, True, False]) 得 2(int)
同一个 sum 换了输入 结果类型自动跟着变,代码 0 处类型声明
写上去的注解 a:int, b:int, return:int
注解会被运行时检查吗 否(只记录,不阻断)
注解会被自动推断吗 否(a + b 不会倒推出 int)
强行传字符串 'xy'
这段代码在静态语言里要写的类型 4 处(参数 a、b、返回值、变量 s)
在 Python 里实际写的类型 0 处(注解可省)
**最大的区别在【什么时候知道类型】**:静态语言编译时知道,**Python 跑到那一行才知道。**
**所以【自动推断】的好处不是省字,是省事**——**但代价是错误也推迟到运行时。**
**注解在 Python 里是文档,不是保证**:`typed("x", "y")` 照样跑出 `xy`。
实测:sum 换输入类型自动跟变,代码 0 处类型声明。
Python 的注解只是记录:不检查、不推断,typed("x","y") 照样跑出 xy。
真正的类型推断在编译期就把类型算出来——像动态语言一样好写,却能在编译期拦错。
30 秒实验
- 给一个函数写全注解,再用错误类型调一次,看它会不会拦住。
- 想想为什么动态语言的错误总是跑到线上才炸。
常见误区
- 以为写了注解就有类型检查。Python 注解默认什么都不做。
- 把动态类型当成【没有类型】。类型还在,只是运行时才看。
自测三问(点开对答案)
1. sum 换输入后类型变吗? —— 变:整数得 int,浮点得 float,布尔得 int。 2. Python 的注解会被检查吗? —— 不会,只记录在 __annotations__ 里,也不做推断。 3. 类型推断的代价? —— 推断不出来时要手写注解,报错信息常晦涩。- ☐ A.10 对7个特征的总结
相关笔记
- 特征6:使用模式匹配和递归来编写循环处理 — 上一条
- 对7个特征的总结 — 打分收口
- 通过嵌入类型使程序员的工作变轻松 — 类型在 OOP 里的用法
- 🧩 面向对象是怎样工作的 · 索引