高级特性
前面学了变量、控制流程、函数和推导式,这一篇来聊聊 Python 里几个让代码更短、更优雅的高级特性:切片、迭代和迭代器。它们的核心思想都差不多——用更少的代码表达更清晰的意图。
1. 切片
切片(slice)是专门用来「截取序列某一段」的语法。列表、元组、字符串这些有序序列都可以切片,写法非常统一。
1.1 基本写法
切片用方括号 [] 表示,格式是 [start:end],其中 start 包含,end 不包含,和 range() 的规则一样。
names = ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack']
# 取前 3 个元素
print(names[0:3]) # ['Michael', 'Sarah', 'Tracy']
# start 为 0 时可以省略
print(names[:3]) # ['Michael', 'Sarah', 'Tracy']
# 从索引 1 取到 3(不含 3)
print(names[1:3]) # ['Sarah', 'Tracy']nums = list(range(10)) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# 取前 5 个
print(nums[:5]) # [0, 1, 2, 3, 4]
# 取第 5 到第 8 个(不含第 8 个)
print(nums[5:8]) # [5, 6, 7]
# 取第 3 个开始到最后
print(nums[3:]) # [3, 4, 5, 6, 7, 8, 9]1.2 负数索引
切片也支持负数索引:-1 表示倒数第一个,-2 表示倒数第二个,以此类推。
nums = list(range(10))
# 取最后 3 个
print(nums[-3:]) # [7, 8, 9]
# 取倒数第 3 个(不含)到倒数第 1 个(不含)
print(nums[-3:-1]) # [7, 8]
# 取除最后 2 个以外的所有元素
print(nums[:-2]) # [0, 1, 2, 3, 4, 5, 6, 7]负数索引其实就是「从后往前数」,记忆负担很小,但非常实用。
1.3 步长
切片还支持第三个参数 step,格式是 [start:end:step],表示每隔几个取一个。
nums = list(range(10))
# 前 10 个,每 2 个取一个
print(nums[:10:2]) # [0, 2, 4, 6, 8]
# 所有元素,每 3 个取一个
print(nums[::3]) # [0, 3, 6, 9]
# 取所有元素,步长为 1,相当于复制整个列表
print(nums[::]) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]1.4 字符串和元组切片
字符串和元组本质上也是序列,所以同样支持切片。切完后的类型和原类型保持一致。
# 字符串切片
s = 'ABCDEFG'
print(s[:3]) # 'ABC'
print(s[::2]) # 'ACEG'
print(s[::-1]) # 'GFEDCBA'
# 元组切片
t = (0, 1, 2, 3, 4, 5)
print(t[:3]) # (0, 1, 2)
print(t[::2]) # (0, 2, 4)1.5 常见技巧
切片里藏着不少「一行搞定」的小技巧。
nums = [1, 2, 3, 4, 5]
# 复制列表
new_nums = nums[:]
print(new_nums) # [1, 2, 3, 4, 5]
# 反转列表
print(nums[::-1]) # [5, 4, 3, 2, 1]
# 删除列表最后两个元素
nums = nums[:-2]
print(nums) # [1, 2, 3]步长为负数时,切片会从后往前取。
[::-1]是最常见的反转写法。
2. 迭代
遍历在 Python 里有一个更专业的说法——迭代。只要对象是可迭代的,就能用 for...in 逐个取值。
2.1 for...in 循环
Python 的 for 循环不像 C 语言那样依赖下标,它直接遍历集合里的元素。
names = ['Michael', 'Sarah', 'Tracy']
for name in names:
print(name)
# 输出:
# Michael
# Sarah
# Tracyfor ch in 'ABC':
print(ch)
# 输出:
# A
# B
# C这种写法不关心索引,只关心元素本身,代码更自然。
2.2 遍历 dict
字典也可以迭代,默认遍历的是 key。
scores = {'语文': 85, '数学': 92, '英语': 78}
# 默认遍历 key
for subject in scores:
print(subject)
# 遍历 value
for score in scores.values():
print(score)
# 同时遍历 key 和 value
for subject, score in scores.items():
print(f'{subject}: {score}')字典的遍历顺序取决于插入顺序(Python 3.7+),所以看到结果不一定按字母顺序。
2.3 遍历 list 和 set
列表和集合都支持 for...in,区别在于集合会自动去重且没有顺序。
# 遍历列表
nums = [1, 2, 3, 4, 5]
for n in nums:
print(n)
# 遍历集合
s = {1, 2, 3, 3, 3}
for n in s:
print(n)
# 输出: 1, 2, 3(重复的 3 只出现一次)2.4 判断对象是否可迭代
可以用 collections.abc.Iterable 来判断一个对象是不是可迭代的。
from collections.abc import Iterable
print(isinstance('abc', Iterable)) # True
print(isinstance([1, 2, 3], Iterable)) # True
print(isinstance({'a': 1}, Iterable)) # True
print(isinstance(123, Iterable)) # False2.5 结合 enumerate
有时候既想要索引又想要元素,可以用 enumerate() 函数。
fruits = ['apple', 'banana', 'cherry']
for index, fruit in enumerate(fruits):
print(f'{index}: {fruit}')
# 输出:
# 0: apple
# 1: banana
# 2: cherryenumerate() 还可以指定起始索引。
for index, fruit in enumerate(fruits, start=1):
print(f'{index}. {fruit}')
# 输出:
# 1. apple
# 2. banana
# 3. cherry
for循环里同时用两个变量接收值,在 Python 里很常见,比如遍历dict.items()时用for k, v in d.items()。
3. 迭代器
理解了「可迭代对象」,再来看一个更具体的概念:迭代器(Iterator)。
3.1 Iterable 和 Iterator 的区别
- Iterable(可迭代对象):可以用
for...in遍历的对象,比如list、tuple、dict、set、str、generator等。 - Iterator(迭代器):可以被
next()调用,并返回下一个值的对象。
简单说:所有迭代器都是可迭代对象,但不是所有可迭代对象都是迭代器。
from collections.abc import Iterable, Iterator
# 列表是可迭代对象,但不是迭代器
print(isinstance([1, 2, 3], Iterable)) # True
print(isinstance([1, 2, 3], Iterator)) # False
# 生成器既是可迭代对象,也是迭代器
print(isinstance((x for x in range(3)), Iterable)) # True
print(isinstance((x for x in range(3)), Iterator)) # True3.2 iter() 和 next()
可以用 iter() 函数把一个可迭代对象转换成迭代器,再用 next() 逐个取值。
nums = [1, 2, 3]
it = iter(nums)
print(next(it)) # 1
print(next(it)) # 2
print(next(it)) # 3
# 元素取完了再调用 next() 会抛出 StopIteration
# print(next(it)) # StopIteration也可以手动捕获 StopIteration 异常来结束遍历。
it = iter([1, 2, 3])
while True:
try:
print(next(it))
except StopIteration:
print('没有更多元素了')
break
# 输出:
# 1
# 2
# 3
# 没有更多元素了
StopIteration不是错误,它只是迭代器「耗尽」的信号,代表遍历结束。
3.3 迭代器为什么重要
迭代器的好处是惰性计算:每次只计算下一个值,而不是一次性把所有元素都塞进内存。
# 列表会把 1000 万个数字都存到内存里
big_list = [x for x in range(10000000)]
# 生成器只保存生成规则,按需取值
big_gen = (x for x in range(10000000))因此迭代器特别适合处理大文件、大数据流、无限序列等场景。
3.4 与生成器的关系
生成器(generator)本质上就是一种迭代器。它可以用生成器表达式创建,也可以用 yield 关键字定义生成器函数。生成器的内容在前面的推导式中已经详细展开,本篇就不再重复了。
列表生成式同样也在推导式里讲过了,这里只需要记住:它和切片、迭代器一样,都是 Python 让代码更短、更高效的工具之一。
相关信息
- 切片
[:]可以截取列表、元组、字符串的某一段,支持负数索引和步长;[::-1]是最常用的反转写法。 for...in可以遍历任何可迭代对象,包括list、tuple、dict、set、str等;enumerate()可以让我们在遍历时同时拿到索引和元素。- 迭代器
Iterator比可迭代对象Iterable更进一步,可以被next()逐个取值,元素耗尽时抛出StopIteration。 iter()可以把可迭代对象转成迭代器;生成器是迭代器的一种,具有惰性计算、节省内存的优势。- 列表生成式详细内容请参考推导式,生成器函数和
yield也放在推导式章节里介绍了。