第 2 章 · 变量、类型与字符串

Python 的类型观和 C/Java 截然不同:变量是「名字贴」,类型跟着对象走。这一章讲动态类型的真实含义(以及它怎么坑人)、Python 的基本类型、以及 f-string——顺带把「一切皆对象」这个贯穿全课的世界观立起来。

2.1 变量:名字贴,不是盒子

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 社区的成熟路线。

2.2 基本类型速览

类型例备注
int42, 10**100任意精度,永不溢出!(计组课第 2 章溢出问题不存在)
float3.14, 1e-5双精度浮点,0.1+0.2 != 0.3 照旧(计组课第 2 章 IEEE 754)
boolTrue, False是 int 的子类,True == 1 可参与运算(冷知识)
str"你好", 'abc'不可变(改 = 建新串),Unicode 原生
NoneTypeNone「没有值」,判空用 is None

int 任意精度是个真实的甜点:2**100 直接出 31 位数字,阶乘、大数运算不炸——代价是性能(int 是堆对象,算术要拆箱,计组课第 6 章 malloc 的税)。浮点的坑全人类共用:0.1 + 0.2 == 0.3 为 False,金额用 decimal.Decimal 或整数分。

2.3 字符串:Python 最丰富的类型

字符串是「字符的不可变序列」,操作密度全语言最高:

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 路径必备)。

2.4 f-string:格式化的现代答案

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+)。

2.5 一切皆对象:世界观的建立

dir(42) 试试:整数身上挂着几十个方法与属性(可 .bit_length()、有 __add__)。Python 里函数、类、模块全都是对象,可以赋值、传参、放进列表——这是第 8 章(函数是一等公民)、第 9 章(装饰器)的公理。对比 C(数据是数据,函数是函数)和 Java(部分对象),Python 的「皆对象」是最彻底的一档,它让语言的「元能力」(把代码当数据处理)门槛极低。

常见错误

  • 可变默认参数。def f(x, lst=[]):默认列表在函数定义时创建一次,多次调用共享同一份(第 3 章细讲,先记住默认值用 None 哨兵)。
  • is 和 == 混用。== 比值,is 比身份(同一对象);判 None 用 is None(惯用法),比数字/字符串一律 ==。
  • 字符串修改失败。s[0] = "H" 直接 TypeError(不可变);「改」= 切片拼接或转列表再 join。
  • 整数与字符串直接相加。"age: " + 18 TypeError;显式 str(18) 或 f-string。

练习

  1. 动手:在 REPL 里做「名字贴实验」:a = [1,2]; b = a; b.append(3); print(a)(a 也变了!列表是可变对象,第 4 章的主角);再用 id() 看 a、b 的身份,理解 2.1。
  2. 动手:字符串练习:给定 "python is fun",分别输出:大写、首字母大写、单词倒序排列后的句子、每个单词的长度。全部用一句话(链式或推导式)完成。
  3. 动手:用 f-string 打印一张 9×9 乘法表(对齐用 :>4),和 Java 课第 4 章的同题对比代码量。
  4. 思考:「int 任意精度」和「float 固定精度」在同一语言共存。写代码验证 0.1 + 0.2 != 0.3 且 1/3 * 3 == 1.0 为 False,再想:为什么 Python 不把 int 也做成固定 64 位(提示:易用性 vs 性能,Python 的取舍偏向哪边,代价是什么)?