要点
赋值把名字绑定到对象,两个名字可以指向同一个列表。不可变值的重新绑定与可变对象的原地修改需要分开理解。
1. 变量:贴在对象上的标签
Python 变量没有类型,对象才有。赋值是把名字绑定到对象:
x = 42 # 名字 x 指向 int 对象 42
x = "hello" # 现在 x 指向 str 对象 —— 完全合法
与 Go/Java 的区别:int x = 42 把盒子填成整数,之后只能装整数;而 Python 的 x 只是一个可反复改贴的标签。
a = [1, 2, 3]
b = a # b 与 a 指向同一个列表
b.append(4)
print(a) # [1, 2, 3, 4] —— 同一对象被两个名字观察
这是 Python 新手第一大陷阱:赋值复制的是引用。需要独立副本时用
b = a.copy()或b = list(a)。
动态类型的代价是错误晚发现,所以现代 Python 靠类型注解找回编译期体验(见第 4 节)。
2. 六种常见基础类型
| 类型 | 示例 | 可变 | 说明 |
|---|---|---|---|
| int | 42 | 否 | 任意精度整数,2**100 也精确 |
| float | 3.14 | 否 | IEEE 754 双精度,0.1 + 0.2 != 0.3 |
| str | “python” | 否 | Unicode 文本,任何”修改”都返回新对象 |
| bool | True / False | 否 | int 的子类,True == 1 |
| list | [1, 2, 3] | 是 | 有序可变序列,b = a 复制的是引用 |
| dict | {“py”: 2} | 是 | 键值映射,3.7 起保持插入顺序 |
不可变(immutable) 意味着任何”修改”操作都返回新对象:
s = "python"
s.upper() # 返回 'PYTHON',s 本身不变
s = s.upper() # 必须重新绑定才"改变" s
print(10 / 3) # 3.3333333333333335 真除法,永远得 float
print(10 // 3) # 3 整除
print(10 % 3) # 1 取余
print(2 ** 100) # 1267650600228229401496703205376 大整数无上限
浮点精度是 IEEE 754 的固有行为:0.1 + 0.2 == 0.3 结果为 False。金额计算用 decimal.Decimal,科学计算用 float。
3. f-string:现代格式化的唯一答案
Python 3.12 起 f-string 支持嵌套引号与多行表达式,日常输出仍是首选的字符串插值方案:
name, price, qty = "机械键盘", 399.5, 2
1. 基本插值:{表达式}
print(f"{name} 单价 {price} 元")
1. 表达式直接计算
print(f"合计 {price * qty:.2f} 元") # 合计 799.00 元
1. 对齐与填充:{值:填充符 对齐 宽度}
print(f"|{name:<10}|") # 左对齐,宽 10
print(f"|{price:>10.1f}|") # 右对齐,宽 10,保留 1 位小数
print(f"|{name:*^10}|") # 居中,* 填充
1. 调试三件套:= 语法输出表达式本身
print(f"{price=}, {qty=}") # price=399.5, qty=2
1. 千分位与百分号
print(f"{1234567:,}") # 1,234,567
print(f"{0.856:.1%}") # 85.6%
多行 f-string 与嵌套引号(3.12 新能力):
data = {"user": {"name": "ada"}}
print(f"用户名: {data["user"]["name"]}") # 3.12 起允许内部再用双引号
3.14 新增 t-string(PEP 750):t"..." 语法与 f-string 同形,但返回的不是 str,而是可延迟处理的模板对象——渲染交给库决定,适合 HTML 转义、SQL 参数化等防注入场景:
title = "<script>攻击</script>"
template = t"<h1>{title}</h1>" # 模板对象,不会立即拼接
1. 由渲染库(如 html 支持库)决定如何转义 title 后再输出
入门阶段记住”f-string 直接出字符串、t-string 出模板对象”即可;完整格式化语法见 Python 官方文档中的 String Formatting。
旧写法 % 与 str.format() 只需能读懂,新代码一律 f-string(需要防注入的模板渲染交给 t-string 生态)。
4. 类型注解:给动态类型上保险
注解不影响运行时,但编辑器、mypy/pyright、FastAPI 都依赖它:
# 变量注解(多在复杂初始化处使用)
count: int = 0
tags: list[str] = []
config: dict[str, str] = {}
1. 函数注解是刚需
def repeat(text: str, times: int = 2) -> str:
return text * times
1. 可能为空 → 管道联合类型(3.10+ 标准写法)
def find_user(uid: int) -> str | None:
...
类型演进对照(旧代码里会遇到,新代码用右列):
| 旧写法(3.8 及以前) | 3.10+ 写法(3.12 仍推荐) |
|---|---|
| Optional[str] | str | None |
| Union[int, str] | int | str |
| List[int] / Dict[str, int] / Tuple[A, B] | list[int] / dict[str, int] / tuple[A, B] |
| Callable[[int], str](从 typing 导入) | collections.abc.Callable[[int], str] |
| Vector: TypeAlias = list[float] | type Vector = list[float](3.12+) |
注解是”承诺”而非”强制”:
def f(x: int)传字符串不会在运行时报错,需靠uvx mypy .或 Pylance 静态检查兜底。
5. 类型转换与真值判断
int("42"), float("3.5"), str(99), bool("") # 显式转换
int("4A") # ValueError —— 转换失败抛异常而非返回垃圾值
1. 真值测试:以下都为 False
falsy = [0, 0.0, "", [], {}, set(), None, False]
习惯写法是利用真值语义,而不是与空值硬比较:
items: list[str] = []
if items: # 推荐:非空列表即真
print("有数据")
name: str | None = None
if name: # 同时排除 None 和 ""
print(name.strip())
最佳实践
类型注解描述预期契约,运行时仍可收到不符合约定的值。判断“缺失”用 is None,判断“为空”可用相应长度或真值;0、空串与 None 不能因都为假就无条件视为同一业务情况。
浮点近似比较要设置符合场景的相对或绝对容差,精确可表示值与特定协议也可能需要精确比较。f-string、format 和日志的延迟格式化各有用途,不必统一禁用。练习分别输入 None、0 和空串,确认不会误用默认值。
常见问题
Q1: 为什么 b = a 之后改 b,a 也变了?
A: a、b 是同一对象的两个标签(第 1 节)。可变对象(list/dict/set)共享引用;int/str 等不可变对象”看似复制”是因为修改只能产生新对象。
Q2: 注解错了运行时为什么没报错?
A: 注解默认只存进 __annotations__,解释器不校验。运行时强校验交给 Pydantic。
练习与实践
正文验证:绑定、不可变值与格式化
下面程序用最小示例验证本页的关键对象模型与 f-string 规则,可直接复制运行。
a = [1]
b = a
b.append(2)
assert a == [1, 2]
b = [9]
assert a == [1, 2] and b == [9]
name, price, qty = 'keyboard', 399.5, 2
assert f'{price * qty:.2f}' == '799.00'
assert f'{name:*>10}' == '**keyboard'
assert 0.1 + 0.2 != 0.3
print('python-bindings: alias, rebinding, f-string, float')
练习一:类型观察
- 在 REPL 中依次执行
type(1)、type(True)、isinstance(True, int),解释输出 - 构造
a = [1, 2],用三种方式创建独立副本并逐一验证
练习二:格式化报告
- 给定
items = [("键盘", 399.5, 2), ("鼠标", 129, 5)] - 用 f-string 输出右对齐的三列表格,金额保留两位、千分位
- 合计行用
=调试语法打印各变量
评估标准:输出形如 | 键盘 | 399.50 | 2 |,且 uvx mypy 对练习代码零报错。
延伸阅读
- Python 函数与面向对象——接着是函数与面向对象
查看完整学习路线——这篇属于该系列,按路线顺序学效果更好。