名字、值、字符串与控制流
目标:能够用 Python 的“名字绑定到对象”模型解释赋值、条件和循环,并写出一个可测试的文本分类函数。
来自 C++、Rust 或 Go 时,很容易把 Python 变量想成带固定存储槽和编译期类型的盒子。更准确的模型是:名字在某个作用域中绑定到对象。对象有类型和值;名字可以在之后绑定到另一个对象。
count = 3 # 名字 count 绑定到 int 对象
count = "three" # 同一个名字改为绑定 str 对象
这不代表“对象的类型变了”。整数 3 仍然是整数,只是 count 不再指向它。类型标注能让静态检查器警告这种变化,但默认运行时不会阻止。
Python 的 if 不要求显式布尔类型,而是调用 truth-value 规则:零、空容器、空字符串和 None 通常为假。工程代码里要区分“为空”和“缺失”:if text 会同时把 "" 当作假,而 if text is None 只判断缺失。
完整示例:分类一条文本请求
保存为 text_rules.py:
from __future__ import annotations
def classify(text: str, urgent: bool = False) -> str:
cleaned = text.strip()
if not cleaned:
return "empty"
if urgent or "退款" in cleaned:
return "high"
if len(cleaned) >= 20:
return "normal"
return "low"
def describe(text: str, urgent: bool = False) -> str:
level = classify(text, urgent)
return f"{text=!r} -> level={level}"
if __name__ == "__main__":
samples = ["", "请退款", "这是一条长度超过二十个字符的普通客户消息,用来测试分类"]
for index, sample in enumerate(samples, start=1):
print(f"{index}. {describe(sample)}")
运行:
uv run python text_rules.py
预期输出:
1. text='' -> level=empty
2. text='请退款' -> level=high
3. text='这是一条长度超过二十个字符的普通客户消息,用来测试分类' -> level=normal
这里有几项现代而常用的写法:
- f-string 的
{text=!r}同时打印表达式名和repr,便于看见空格与空字符串。 enumerate(..., start=1)直接生成序号和值,避免手动维护索引。- 先处理特殊情况并
return,减少深层elif嵌套。 in对字符串做子串测试;它表达的是业务规则,不是正则表达式。
和 Go/Rust 的关键差异
Python 的缩进是语法,不使用花括号;for 直接消费 iterable,而不是只提供 C 风格计数循环。range(3) 产生 0, 1, 2,右端不包含。条件表达式写成 a if condition else b,阅读顺序和 C 的 condition ? a : b 不同。
引导实验:找出过宽的 truthiness
把 classify 的签名暂时改成 text: str | None,然后在函数第一行写:
if not text:
return "empty"
分别调用 classify(None) 与 classify("")。两者都得到 empty。如果业务需要区分“字段没有提供”和“提供了空文本”,应改成:
if text is None:
return "missing"
cleaned = text.strip()
if not cleaned:
return "empty"
这就是条件写法背后的决策:先问业务状态是否需要区分,再选择 truthiness 或显式比较。
独立练习
扩展 classify:
None返回missing;- 仅空白返回
empty; - 包含“退款”或
urgent=True返回high; - 其他文本按长度分类。
验收断言:
assert classify(None) == "missing"
assert classify(" ") == "empty"
assert classify("我要退款") == "high"
assert classify("短消息") == "low"
参考答案
def classify(text: str | None, urgent: bool = False) -> str:
if text is None:
return "missing"
cleaned = text.strip()
if not cleaned:
return "empty"
if urgent or "退款" in cleaned:
return "high"
return "normal" if len(cleaned) >= 20 else "low"
排错路线
- f-string 看不到首尾空格:使用
!r,不要只打印字符串本身。 - 长度边界和预期差一:把
len(cleaned)打印出来,确认是否先strip()。 None上出现AttributeError:说明.strip()发生在缺失检查之前。