返回首页
## 引言
这是第二章「变量、类型与运算符」的最后一篇,也是收卷篇。前十篇依次建立了:变量与引用的真相、数字类型画像、布尔与真值测试、字符串基础、f-string 格式化、类型转换、运算符优先级、位运算、命名规范。本篇做三件事:第一,用 10 道选择题把你拉回每个知识点的"精确版本"——选择题的价值不在答对,而在把你以为懂的东西逼出原形;第二,用一道大题(进制转换器)把字符串、数字、运算符、转换、命名全部串进一个可运行的程序;第三,给出两道挑战练习,为第三章流程控制热身。建议的作答方式:先闭卷写出每题的答案与理由,再对照解析——理由比答案重要,答对但理由写错,说明该篇需要回读。
## 测验说明
10 道题覆盖全章 10 篇,每题标注出处。选项均为单选。做完全部题目后,再阅读解析部分;解析会指出该题对应回读哪一篇、哪个小节的哪一个概念。
### 第 1 题(出处:第 1 篇 变量与赋值)
```python
a = 10
b = a
a = 20
print(b)
```
输出是?(A)10 (B)20 (C)报错 (D)15
### 第 2 题(出处:第 2 篇 数字类型)
下列哪个说法正确?(A)`1 / 2` 的结果是整数 0 (B)`1e3` 的类型是 `int` (C)`10 ** 100` 的类型是 `int` (D)`1 + 2j` 是一个浮点数
### 第 3 题(出处:第 3 篇 布尔与真值)
`sum([True, False, True])` 的值是?(A)3 (B)2 (C)`[True, False, True]` (D)报错,布尔不能求和
### 第 4 题(出处:第 4 篇 字符串基础)
`len(r"\n")` 与 `"ab" * 3` 的结果分别是?(A)1 与 `"ababab"` (B)2 与 `"ababab"` (C)2 与 `"aaaabbbb"` (D)1 与 `"aaabbb"`
### 第 5 题(出处:第 5 篇 f-string)
`f"{3.14159:.2f}"` 的输出是?(A)`"3.14"` (B)`"3.15"` (C)`"3.14159"` (D)报错,格式说明符不支持
### 第 6 题(出处:第 6 篇 类型转换)
下列哪个调用会抛出 `ValueError`?(A)`int("10", 2)` (B)`float("1e3")` (C)`int("3.14")` (D)`int("0xFF", 0)`
### 第 7 题(出处:第 7 篇 运算符与优先级)
`2 ** 3 ** 2` 与 `-2 ** 2` 的值分别是?(A)512 与 -4 (B)64 与 -4 (C)512 与 4 (D)64 与 4
### 第 8 题(出处:第 8 篇 位运算)
`5 & 3`、`~5`、`0b1010 | 0b0101` 的值分别是?(A)1、-6、15 (B)1、6、15 (C)7、-6、15 (D)1、-6、6
### 第 9 题(出处:第 9 篇 命名规范)
下列哪个写法正确?(A)`True = 1` (B)`def list(x): return x` (C)`9score = 100` (D)`class_ = "math"`
### 第 10 题(出处:第 6/7/8 篇 综合)
`int("3A", 16)`、`5 >> 1`、`234 // 7` 的值分别是?(A)58、2、33 (B)56、2、33 (C)58、3、33 (D)58、2、34
### 计分与回读建议
10 题每题 10 分,答完先别翻解析,按下表对照自己的分数段安排复习:8 分以下建议重读对应错题的篇目后再做挑战练习;9~10 分可以直接进实战。分数只能说明"哪些点没建起来",回读动作才真正补齐——错题对应篇目一览:
| 题号 | 考点 | 回读篇目 |
|---|---|---|
| 1 | 变量绑定与引用 | 第 1 篇「名字是引用」、不可变对象 |
| 2 | 整数/浮点/复数识别 | 第 2 篇类型画像 + 第 6 篇隐式转换 |
| 3 | 布尔参与算术 | 第 6 篇易错点 2 |
| 4 | 原始字符串与重复 | 第 4 篇转义与字面量 |
| 5 | 格式说明符 | 第 5 篇 f-string 精度控制 |
| 6 | 字符串转数字边界 | 第 6 篇 ValueError 边界 |
| 7 | 幂的右结合与优先级 | 第 7 篇优先级表等级 4/5 |
| 8 | 位运算三运算 | 第 8 篇 `&`/`~`/`\|` 小节 |
| 9 | 关键字与遮蔽 | 第 9 篇语法关与冲突关 |
| 10 | 进制、移位、地板除 | 第 6/7/8 篇对应小节 |
## 解析:每题为什么
**第 1 题:A。** `b = a` 让 b 与 a 绑定同一个 int 对象 10;`a = 20` 是让 a 重新绑定到新对象 20,b 的引用不受影响。int 不可变,赋值即重绑定——这正是第 1 篇"名字是对象的引用"的直译。
**第 2 题:C。** `/` 恒返回 float(`1 / 2` 是 `0.5`),`1e3` 是 float 的科学计数法字面量;只有 `**` 在整数底数整数指数时保持 int,`10 ** 100` 是大整数。`1 + 2j` 是 complex。回看第 2 篇的"int/float/complex 画像"与第 7 篇"`/` 恒得 float"。
**第 3 题:B。** `True` 是 `int` 的子类,参与算术时当 1 用,`sum` 从 0 开始累加:1+0+1 = 2。这正是第 6 篇"布尔混入算术"的易错点 2。
**第 4 题:B。** 原始字符串 `r"\n"` 中的 `\n` 不转义,是两个字符(反斜杠加 n),长度 2;`"ab" * 3` 是字符串重复,得 `"ababab"`。回看第 4 篇的转义与原始字符串。
**第 5 题:A。** `.2f` 表示两位小数的定点表示,`3.14159` 按**舍入到最近**得 `3.14`;`f"{3.14159:.2f}"` 输出 `"3.14"`。注意格式化用的是 round-to-nearest 而非截断(联系第 6 篇银行家舍入话题)。
**第 6 题:C。** `int()` 只认整数语法,`"3.14"` 含小数点直接 `ValueError: invalid literal for int() with base 10: '3.14'`。A 的 `int("10", 2)` 是按二进制解释 = 2,D 的 base=0 按前缀推断 = 255,B 的 `float("1e3")` 合法 = 1000.0。回看第 6 篇"ValueError 边界"。
**第 7 题:A。** `**` 右结合:`2 ** (3 ** 2) = 2 ** 9 = 512`;`**` 优先级高于一元负号:`-(2 ** 2) = -4`。回看第 7 篇优先级表等级 4 与 5 及其推导。
**第 8 题:A。** `5 & 3`:`101 & 011 = 001` = 1;`~5 = -5 - 1 = -6`(补码推论);`0b1010 | 0b0101 = 0b1111` = 15。第 8 篇的"掩码与置位"正是 `&` 与 `|`。
**第 9 题:D。** `True` 是关键字,赋值报 `SyntaxError: cannot assign to True`;以数字开头(`9score`)非法;`def list(...)` 虽语法合法,但遮蔽内置 `list`,属第 9 篇明令避免的坑。`class_` 尾部下划线绕行是标准姿势。
**第 10 题:A。** `int("3A", 16)` = 3×16 + 10 = 58;正数右移是整除 2 的幂,`5 >> 1` = 2;`234 // 7`:7×33 = 231,商 33,余 3。三道小题分别对应进制解析、移位、地板除,正是本篇实战要自动化的三类计算。
### 速转技巧:二进制与十六进制的四位一组
十进制转十六进制用不上的时候,二进制与十六进制之间的互转有省心捷径:**每 4 位二进制对应 1 位十六进制**(16 = 2⁴)。`0b1111` 对应 `"F"`、`0b1010` 对应 `"A"`。所以 `0x3A` 拆开看就是 `0011` + `1010`,反过来 `11111111` 从右往左每四位一组 `1111 1111`,直接读出 `0xFF`。这个"8421 码"(8+4+2+1 构成一位十六进制)让你在调二进制协议时不用拿起计算器:
```python
# 8421 速转验证:0b1011_1100 == 0xBC
for nibble_value, want in (("1011", "B"), ("1100", "C")):
got = hex(int(nibble_value, 2))[2:].upper()
print(nibble_value, "->", got, got == want) # 1011 -> B True
```
八进制与二进制的对应关系则是三位一组(8 = 2³),同理可推。这个技巧是第 8 篇"位是数据的原生形态"的延续——字节(8 位)正好两个十六进制位,网络抓包工具里清一色的十六进制显示,根源在此。
## 综合实战:进制转换器
选择题到此为止,现在进入"能跑起来的测验":一个支持**2/8/10/16 进制互转**的交互式命令行工具。它把全章知识用了个遍——字符串切片(第 4 篇)、整除与取余 `divmod`(第 7 篇)、`try/except` 的 ValueError 边界(第 6 篇)、命名规范(第 9 篇)。
### 核心函数:十进制转任意进制
转换算法一句话:**反复除以目标进制,余数倒序排列**。整数除法留商,`%` 留余——这正是第 7 篇 `a == (a // b) * b + a % b` 恒等式的工程应用:
```python
DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
def to_base(n: int, base: int) -> str:
"""十进制整数转 2~36 进制字符串,支持负数。"""
if not 2 <= base <= 36:
raise ValueError(f"不支持的进制:{base}")
sign = "-" if n < 0 else ""
n = abs(n)
digits = []
while n:
n, r = divmod(n, base)
digits.append(DIGITS[r])
return sign + "".join(reversed(digits)) if digits else "0"
```
`DIGITS` 是常量(全大写,第 9 篇),下标即数值:`DIGITS[10]` 是 `"A"`。`divmod` 一次给商与余,`reversed` 倒序拼装,`n = 0` 时返回 `"0"`。36 进制封顶是因为 `DIGITS` 恰好 36 个字符——与 `int(s, base)` 的 base 上限(2~36)严格一致。
### 反向:任意进制字符串转十进制
```python
def from_base(s: str, base: int) -> int:
"""2~36 进制字符串转十进制整数,逐字符合法校验。"""
s = s.strip()
sign = 1
if s.startswith("-"):
sign, s = -1, s[1:]
elif s.startswith("+"):
s = s[1:]
if not s:
raise ValueError("输入为空")
value = 0
for ch in s.upper():
d = DIGITS.find(ch)
if d < 0 or d >= base:
raise ValueError(f"字符 '{ch}' 不是 {base} 进制数字")
value = value * base + d
return sign * value
```
逐位累加是**霍纳法则(Horner's method)**:`"3A"` 读作 `((3) × 16 + 10)`。`DIGITS.find(ch)` 返回下标即数值,`find` 找不到返回 -1,恰好与"非法字符"判定合体。`d >= base` 拦截"数字存在但进制太小"的情形——`"2"` 在二进制里非法,正如测验第 6 题的边界讨论。
### 主循环:交互式回收站
```python
def convert():
"""交互主循环:源进制 -> 数字 -> 目标进制。"""
while True:
cmd = input("请输入源进制(2/8/10/16,q 退出): ").strip().lower()
if cmd == "q":
print("再见!")
return
if cmd not in ("2", "8", "10", "16"): # 白名单校验
print("仅支持 2/8/10/16 进制")
continue
src = int(cmd)
raw = input("请输入要转换的数字: ").strip()
try:
value = from_base(raw, src)
except ValueError as e:
print(f"输入无效:{e}") # 非法数字不崩溃
continue
dst = input("请输入目标进制(2/8/10/16): ").strip()
if dst not in ("2", "8", "10", "16"):
print("仅支持 2/8/10/16 进制")
continue
print(f"结果:{raw} (base {src}) = {to_base(value, int(dst))} (base {dst})")
if __name__ == "__main__":
convert()
```
配置文件或命令行程序的标准骨架:**白名单校验**(`cmd not in (...)`,第 3 篇成员测试)→ **转换** → **异常兜底**(`try/except ValueError`,第 6 篇边界)→ **格式化输出**(f-string,第 5 篇)。一串真实运行记录(Python 3.12 实测):
```text
请输入源进制(2/8/10/16,q 退出): 16
请输入要转换的数字: FF
请输入目标进制(2/8/10/16): 10
结果:FF (base 16) = 255 (base 10)
请输入源进制(2/8/10/16,q 退出): 10
请输入要转换的数字: 255
请输入目标进制(2/8/10/16): 2
结果:255 (base 10) = 11111111 (base 2)
请输入源进制(2/8/10/16,q 退出): 8
请输入要转换的数字: FF
输入无效:字符 'F' 不是 8 进制数字
请输入源进制(2/8/10/16,q 退出): 10
请输入要转换的数字: -42
请输入目标进制(2/8/10/16): 16
结果:-42 (base 10) = -2A (base 16)
请输入源进制(2/8/10/16,q 退出): q
再见!
```
注意三条细节:非法输入被拦截而不是崩溃;负数全程保留符号位;空输入在 `from_base` 里被 `ValueError("输入为空")` 拦下。这些行为全部来自第 6 篇的 ValueError 边界讨论。
### 双向一致性自检
测验性质的程序最怕"看起来对"。`to_base` 与 `from_base` 互为逆运算,可以交叉验证——每个数转出去再转回来必须原样:
```python
for base in (2, 8, 10, 16, 36):
for n in (0, 255, -255, (1 << 32) - 1):
assert from_base(to_base(n, base), base) == n
print("全部 20 组自检通过") # 5 种进制 × 4 个样例
```
`assert`(断言)让不一致直接中断,是第 3 篇"验证型代码"的又一例证。样例特意包含 0、负数与大整数——0 走"空数字"分支,负数走符号分支,`(1 << 32) - 1` 是 4294967295(第 8 篇位运算的 32 位全 1),覆盖最大位宽。
### 诚实地审视这个"成品"
转换器能跑,但要承认它的两个工程短板,这本身就是本章该有的批判眼光:其一,**它没有复用 `int(s, base)` 与 `format()`**——标准库做同样的事只需一行(`format(255, "b")` 得 `"11111111"`);自己实现的价值在**理解机制**而不在生产效率,真实项目里优先用内置,眼里有实现才是进阶者的状态。其二,**输入体验粗糙**:目标进制校验在读取之后才做、没有历史记录、非法输入只提示不引导——这些在第三章学到循环与字符串方法后都可以补。工具能用的判断标准不是"写出来了",而是"边界都测过了":把测验第 10 题的三个数值喂给它,再想想还有什么输入会击穿它。
## 挑战练习
三道题按难度递进,全部用本章 API 就能完成。第一题复习补码观察法:`n & ((1 << bits) - 1)` 把无限位宽的负数截断成 bits 位,正是第 8 篇"掩码截断"技巧;第二题把 to_base、字符串切片与回文判断组合,验证 585 这个"跨进制的巧合";第三题没有现成答案,强迫你拆解十进制小数的位权结构——如果只靠复制粘贴学到这里,这题会卡住你,而这正是它存在的目的。做完再回头读一遍解析,两轮走完,本章的知识点才算真正交割。
1. **补码视图**:写 `twos_complement_view(n, bits=8)`,返回 n 在 bits 位补码下的二进制字符串(提示:`n & ((1 << bits) - 1)`)。验证 `twos_complement_view(-6)` 是 `"11111010"`。
2. **跨进制回文**:写 `is_palindrome_num(n, base)`,判断 n 在给定进制下是否为回文(左右对称)。已知 585 的二进制 `1001001001` 是回文,验证之。
3. **扩展思考**:如果把 `from_base` 的合法输入扩展到**二进制小数**(如 `"101.1"` 表示 5.5),算法与 `DIGITS` 需要哪些改动?先口头设计,再实现(提示:小数点两侧分别处理,右侧的权重是负次幂)。
## 小结
本章十条主线在今天完成闭环:绑定与引用是第一块地基;数字、布尔、字符串三类基本类型各有各的语义;f-string 让输出精确可控;显式转换与隐式转换管着"类型之门";优先级表是表达式的宪章;位运算把整数拆回比特;命名规范让一切努力可维护。进制转换器把其中的字符串、数字、运算符、转换、异常五条线织在一起,而它离"真实工具"还差最后一公里——流程控制。第三章「流程控制」将从 `if/elif/else` 与 `for/while` 讲起,把今天写在主循环里的 `while True` 和 `continue` 正式扶正;读到这里,你写的每段代码都已经具备本章的全部判断力。用测验里的错题做复习清单,我们第三章见。