Python 的类型观和 C/Java 截然不同:变量是「名字贴」,类型跟着对象走。这一章讲动态类型的真实含义(以及它怎么坑人)、Python 的基本类型、以及 f-string——顺带把「一切皆对象」这个贯穿全课的世界观立起来。
C 的变量是「一个装值的格子」(计组课第 10 章:栈上的内存);Python 的变量是「贴在对象上的名字」:
x = 42 # 造一个 int 对象 42,贴上名字 x
y = x # y 贴到同一个对象上
x = "hello" # x 撕下来贴到新对象;y 仍贴着 42
没有类型声明、可以随时换类型(动态类型),因为类型属于对象不属于名字:type(x) 查的是它当前贴的对象。Java 课第 9 章「包装类」的影子:Python 的 int、str 全是对象(带方法:"hi".upper()),连整数都是(所以慢,计组课第 10 章讲过 Python int 是任意精度堆对象)。
动态类型的代价:类型错误延迟到运行时。x + 1 在 x 是字符串时当场 TypeError(而 Java 编译期就拦,Java 课第 2 章)。现代补救:类型注解(type hints)+ mypy 静态检查(第 7 章),「动态类型 + 可选静态标注」是 Python 社区的成熟路线。
| 类型 | 例 | 备注 |
|---|---|---|
| int | 42, 10**100 | 任意精度,永不溢出!(计组课第 2 章溢出问题不存在) |
| float | 3.14, 1e-5 | 双精度浮点,0.1+0.2 != 0.3 照旧(计组课第 2 章 IEEE 754) |
| bool | True, False | 是 int 的子类,True == 1 可参与运算(冷知识) |
| str | "你好", 'abc' | 不可变(改 = 建新串),Unicode 原生 |
| NoneType | None | 「没有值」,判空用 is None |
int 任意精度是个真实的甜点:2**100 直接出 31 位数字,阶乘、大数运算不炸——代价是性能(int 是堆对象,算术要拆箱,计组课第 6 章 malloc 的税)。浮点的坑全人类共用:0.1 + 0.2 == 0.3 为 False,金额用 decimal.Decimal 或整数分。
字符串是「字符的不可变序列」,操作密度全语言最高:
s = "Hello, Python"
s[0] # 'H'(下标从 0,和 C 同款)
s[-1] # 'n'(负数从尾数,Python 特色)
s[7:13] # 'Python'(切片:含头不含尾)
s[::2] # 'Hlo yhn'(步长 2)
len(s) # 13
"py" in s # True(包含判断)
s.upper() # 'HELLO, PYTHON'(原串不动,返回新串)
s.split(", ") # ['Hello', 'Python'](切开成列表)
"-".join(["a","b","c"]) # 'a-b-c'(拼接成串)
切片(slice) 是 Python 的招牌语法:s[start:stop:step],含头不含尾的设计让 s[:i] + s[i:] 恒等于 s(切两半拼回去,数学上优雅)。不可变性的工程含义:拼接大量字符串用 "+=" 循环是 O(n²)(每次造新串),正解 "".join(list)(第 8 章),Java 课第 9 章 StringBuilder 的同款故事。
字符串比较:== 比内容(和 Java 的 == 陷阱不同!Python 重载了 ==,引用比较是 is),排序按 Unicode 码点。三引号 """多行字符串""" 写长文本;原始字符串 r"C:\new\folder" 让反斜杠不转义(正则和 Windows 路径必备)。
name, score = "思源", 92.567
print(f"{name} 的成绩是 {score:.1f}") # 思源 的成绩是 92.6
print(f"表达式也能进:{score * 2:.2f}") # 表达式也能进:185.13
print(f"{'左对齐':<10}|{'右对齐':>10}|") # 左对齐 | 右对齐|
f-string(f = formatted)在字符串前加 f,花括号里直接写表达式,冒号后是格式说明(:.2f 两位小数、:>10 右对齐宽 10)。它取代了 %-formatting 和 .format() 两代前辈,是 3.6+ 的唯一推荐。调试神器:f"{x=}" 输出 x=42(连名字带值,3.8+)。
dir(42) 试试:整数身上挂着几十个方法与属性(可 .bit_length()、有 __add__)。Python 里函数、类、模块全都是对象,可以赋值、传参、放进列表——这是第 8 章(函数是一等公民)、第 9 章(装饰器)的公理。对比 C(数据是数据,函数是函数)和 Java(部分对象),Python 的「皆对象」是最彻底的一档,它让语言的「元能力」(把代码当数据处理)门槛极低。
def f(x, lst=[]):默认列表在函数定义时创建一次,多次调用共享同一份(第 3 章细讲,先记住默认值用 None 哨兵)。== 比值,is 比身份(同一对象);判 None 用 is None(惯用法),比数字/字符串一律 ==。s[0] = "H" 直接 TypeError(不可变);「改」= 切片拼接或转列表再 join。"age: " + 18 TypeError;显式 str(18) 或 f-string。a = [1,2]; b = a; b.append(3); print(a)(a 也变了!列表是可变对象,第 4 章的主角);再用 id() 看 a、b 的身份,理解 2.1。0.1 + 0.2 != 0.3 且 1/3 * 3 == 1.0 为 False,再想:为什么 Python 不把 int 也做成固定 64 位(提示:易用性 vs 性能,Python 的取舍偏向哪边,代价是什么)?