Appearance
08|元组与范围
元组和列表类似,但创建后不能修改。这个限制让元组在某些场景下比列表更合适——作为字典的键、函数返回的多个值、配置项等不需要修改的数据。
range 则是一个专门生成整数序列的对象,常用于循环计数。
一、元组
创建元组
python
coords = (10, 20)
single = (42,) # 单元素元组必须有逗号,(42) 会被解析为括号表达式
empty = ()元组可以省略括号:
python
coords = 10, 20 # 等价于 (10, 20)但省略括号容易和函数参数混淆,建议显式写括号。
不可变性
元组创建后不能增删改元素:
python
t = (1, 2, 3)
t[0] = 10 # TypeError: 'tuple' object does not support item assignment
t.append(4) # AttributeError: 'tuple' object has no attribute 'append'但如果元组内部包含可变对象(如列表),那个可变对象本身仍然可以修改:
python
t = (1, [2, 3])
t[1].append(4) # 可以,修改的是列表,不是元组
print(t) # (1, [2, 3, 4])元组的不可变指的是"元组这个容器的结构不变"——元素的引用不变。引用指向的对象如果是可变的,对象内部仍然可以变化。
索引和切片
元组的索引、切片操作与列表完全相同:
python
t = ("a", "b", "c", "d")
t[0] # 'a'
t[-1] # 'd'
t[1:3] # ('b', 'c')
t[::-1] # ('d', 'c', 'b', 'a')解包
元组最实用的特性是解包——把一个序列拆开赋给多个变量:
python
x, y = (10, 20)
# x = 10, y = 20
# 交换两个变量
a, b = 1, 2
a, b = b, a
# a = 2, b = 1用 * 接收剩余元素:
python
first, *rest = (1, 2, 3, 4)
# first = 1, rest = [2, 3, 4]
first, *middle, last = (1, 2, 3, 4)
# first = 1, middle = [2, 3], last = 4*rest 接收到的始终是列表,即使原序列是元组。
元组作为字典键
元组是不可变的,可以作为字典的键;列表是可变的,不能作为键:
python
cache = {}
cache[("user", 1)] = "Alice" # 可以
cache[["user", 1]] = "Alice" # TypeError: unhashable type: 'list'与列表的选择
| 特性 | 列表 | 元组 |
|---|---|---|
| 可变 | 是 | 否 |
| 性能 | 稍慢 | 稍快 |
| 能否作为字典键 | 否 | 是 |
| 语义 | "一组会变化的数据" | "一组固定的记录" |
不确定时用列表。明确数据不会变、或需要作为字典键时,用元组。
二、range
range 生成整数序列,常用于循环计数。
python
# range(stop):从 0 开始到 stop-1
range(5) # 0, 1, 2, 3, 4
# range(start, stop):从 start 开始到 stop-1
range(1, 6) # 1, 2, 3, 4, 5
# range(start, stop, step):指定步长
range(0, 10, 2) # 0, 2, 4, 6, 8
range(10, 0, -1) # 10, 9, 8, ..., 1range 不是列表,而是一个"惰性"对象——它只在需要时计算下一个值:
python
r = range(5)
type(r) # <class 'range'>
list(r) # [0, 1, 2, 3, 4],转成列表才能看到全部内容惰性意味着占用内存少。range(1_000_000) 不会占用一百万个整数的内存,只在迭代时逐个生成。
range 支持索引和 in 判断:
python
r = range(0, 10, 2)
r[3] # 6
4 in r # True
5 in r # False记忆锚点:元组不可变,单元素必须有逗号 (42,);元组解包是核心特性,*rest 接收剩余元素;元组可作为字典键,列表不能;range 是惰性对象,不占大量内存;range(5) 生成 0-4,range(1, 6) 生成 1-5。