📄 一句话概括
动态语言里类型嵌在运行时——注解存进 __annotations__ 但运行时不检查,传字符串照样返回 3。
📝 本节和前面几节的关系
4.10 收完三大要素,从这里开始讲语言是怎么把这些事做出来的。
一、类型写在哪
静态语言:类型写在代码里。int total = a + b;——不写就编译不过。
动态语言:类型嵌在运行时。total = a + b——类型信息在对象身上(type(obj))。
【嵌入】的意思就是:它本来就在,不需要你再声明一遍。
二、类型注解是什么
Python 从 3.5 起有了注解:def total(shapes: list) -> float: ...。
实测 total.__annotations__ 拿到 {'shapes': list, 'return': float}。
但传一个字符串进去,它照样返回 3。
注解只在开发和静态检查时起作用,运行时不管。
三、注解不是检查
它是给工具看的,不是给解释器看的。 mypy、IDE 的波浪线读的是它;Python 自己不看。 所以【写了注解】和【类型安全】是两件事——前者是文档,后者要工具兜。
四、代价
写起来省事,错起来晚:同一行代码,静态语言在编译期就拦住,动态语言可能跑到那一步才炸。 这不是缺陷,是取舍——省下的声明,用【晚发现】换的。 这正是 4.13 要说【退化】的前半句。
五、动手 Demo:通过嵌入类型使程序员的工作变轻松
给函数写上类型注解,看它被存到了哪里。 再传一个类型不对的参数,看运行时会怎样。
def total(shapes: list) -> float:
return float(len(shapes))
print(total.__annotations__)
print(total("abc")) # 类型不对,照样跑
实跑输出:
--------------------------------------------------------------------
通过嵌入类型使程序员的工作变轻松
--------------------------------------------------------------------
动态语言里,**类型是【嵌】在运行时里的;**静态语言里,**类型是【写】在代码里的。**
Python 从 3.5 起加了**类型注解**——可以不写,也能让工具替你查。
def total(shapes: list) -> float: ...
实测:注解会被存进 `__annotations__`,但**运行时不检查**。
total.__annotations__ → {'shapes': <class 'list'>, 'return': <class 'float'>}
传一个字符串进去会怎样:total("abc") → 3
**注意第二行:传错了它照样跑,返回 3。**
**注解只在开发和静态检查时起作用,运行时不管。**
静态类型(Java) 动态类型(Python)
-----------------------------------------------------------
类型写在哪 代码里 运行时(或注解)
错类型何时发现 编译期 运行时(或工具)
写起来 啰嗦 省事
**【嵌入类型】的意思是:**类型信息本来就嵌在对象里(`type(obj)`),
**所以不需要你再声明一遍。**这也解释了为什么动态语言写起来快、错起来晚。
实测:注解被存进 __annotations__,但传字符串 "abc" 照样返回 3。
注解只在开发和静态检查时起作用,运行时不管——这是动态语言省事与错晚的同一枚硬币。
30 秒实验
- 给一个函数加上类型注解,再故意传错类型,看什么时候才炸。
- 打开 IDE 的静态检查,看它是不是能报出运行时不报的错。
常见误区
- 以为写了注解就有类型检查。解释器根本不看。
- 以为动态类型是没有类型。类型一直在,只是嵌在对象上。
自测三问(点开对答案)
1. 【嵌入类型】是什么意思? —— 类型信息本来就嵌在运行时对象里(type(obj)),所以不需要你再声明一遍。 2. 类型注解读了有用吗? —— 有用但不在运行时:它给静态检查工具和 IDE 看,解释器不检查,传错类型照样跑。 3. 动态类型的代价是什么? —— 写起来省事,错起来晚——静态语言编译期能拦的错,它可能跑到那一步才炸。- ☐ 下一节(4.12 将类作为类型使用)
相关笔记
- 将类作为类型使用 — 类型怎么用
- 原因之一:OOP结构复杂 — 为什么这些细节要讲
- ⌨️ 编程原理 · 索引 — 类型系统的基础
- 🧩 面向对象是怎样工作的 · 索引