丰富的序列
GitHub仓库链接,同步更新。
- 目录
1 内置序列类型概览
Python 标准库提供了丰富的序列类型,可以大致分为以下几类:
- 按序列类型分
- 容器序列:可存放不同类型的项,例如列表(list)、元组(tuple)和双端队列(deque)。
- 扁平序列:可存放一种简单类型的项,例如字符串(str)、字节序列(bytes)和数组(array.array)。
- 按可变性来分:
- 可变序列:可修改其内容,例如列表(list)和字节缓冲区(bytearray)。
- 不可变序列:内容不可修改,例如元组(tuple)和字符串(str)。
1.1 容器序列
# 容器序列
# 列表(list)
my_list = [1, "hello", 3.14]
print(my_list) # 输出: [1, "hello", 3.14]
# 元组(tuple)
my_tuple = (1, "hello", 3.14)
print(my_tuple) # 输出: (1, "hello", 3.14)
# 双端队列(deque)
from collections import deque
my_deque = deque([1, "hello", 3.14])
print(my_deque) # 输出: deque([1, "hello", 3.14])
1.2 扁平序列
# 字符串(str)
my_string = "hello"
print(my_string) # 输出: "hello"
# 字节序列(bytes)
my_bytes = b"hello"
print(my_bytes) # 输出: b"hello"
# 数组(array.array)
import array
my_array = array.array('i', [1, 2, 3])
print(my_array) # 输出: array('i', [1, 2, 3])
1.3 可变序列
# 列表(list)
my_list = [1, 2, 3]
my_list.append(4)
print(my_list) # 输出: [1, 2, 3, 4]
# 字节缓冲区(bytearray)
my_bytearray = bytearray(b"hello")
my_bytearray.append(ord('!'))
print(my_bytearray) # 输出: bytearray(b"hello!")
# 双端队列(deque)
from collections import deque
my_deque = deque([1, 2, 3])
my_deque.appendleft(0)
print(my_deque) # 输出: deque([0, 1, 2, 3])
1.4 不可变序列
# 元组(tuple)
my_tuple = (1, 2, 3)
# my_tuple[0] = 0 # 这行代码会引发 TypeError,因为元组是不可变的
# 字符串(str)
my_string = "hello"
# my_string[0] = 'H' # 这行代码会引发 TypeError,因为字符串是不可变的
# 字节序列(bytes)
my_bytes = b"hello"
# my_bytes[0] = ord('H') # 这行代码会引发 TypeError,因为字节序列是不可变的
了解这些类型之间的区别和联系,有助于我们根据实际需求选择合适的序列类型。
2 列表推导式和生成器表达式
2.1 列表推导式对可读性的影响
列表推导式提供了一种简洁的遍历和生成列表的方法。例如,生成一个平方数列表:
squares = [x**2 for x in range(5)]
print(squares) # 输出: [0, 1, 4, 9, 16]
2.2 与map和filter的比较
列表推导式可以替代map和filter函数,提供更清晰的代码结构。例如,使用列表推导式筛选和转换序列中的项:
# 使用列表推导式
beyond_ascii = [ord(s) for s in '$¢£¥€¤' if ord(s) > 127]
# 使用map和filter
beyond_ascii = list(filter(lambda c: ord(c) > 127, map(ord, '$¢£¥€¤')))
且列表推导式在代码执行时间上也更快。
2.3 笛卡尔积
列表推导式可以根据两个或更多可迭代对象的笛卡儿积构建列表。例如,生成2种颜色和3种尺寸的T恤衫组合:
# 示例:使用列表推导式计算笛卡儿积
colors = ['red', 'green']
sizes = ['S', 'M']
cartesian_product = [(color, size) for color in colors for size in sizes]
print(cartesian_product) # 输出: [('red', 'S'), ('red', 'M'), ('green', 'S'), ('green', 'M')]
2.4 生成器表达式
生成器表达式占用的内存更少,因为它逐个产出项而不是构建整个列表。例如,使用生成器表达式构建一个元组:
symbols = '$¢£¥€¤'
tuple(ord(symbol) for symbol in symbols)
生成器表达式的句法跟列表推导式几乎一样,只不过把方括号换成圆括号而已。
3 元组不仅仅是不可变列表
3.1 用作记录
元组可以用作记录,其中元组中的一项对应一个字段的数据。例如,存储一个学生的相关信息:
# 示例:元组用作记录
student = ('Alice', 21, 'Physics')
print(f"{student[0]} studies {student[2]} and is {student[1]} years old.")
3.2 用作不可变列表
元组作为不可变列表使用时,具有意图清晰和性能优越的优点。例如,元组在内存中占用的空间比列表小:
a = (10, 'alpha', [1, 2])
b = a
b[-1].append(99)
3.3 列表和元组方法的比较
元组支持所有不涉及增删项的列表方法。例如,元组不支持__reversed__方法,但reversed(my_tuple)依然可以工作。
4 序列和可迭代对象拆包
4.1 使用*
获取余下的项
在并行赋值中,可以使用*来捕获余下的项。例如:
# 示例:使用 * 操作符拆包
first, *middle, last = range(10)
print(first, middle, last) # 输出: 0 [1, 2, 3, 4, 5, 6, 7, 8] 9
4.2 在函数调用和序列字面量中使用*
拆包
fun(*[1, 2], 3, *range(4, 7))
4.3 嵌套拆包
metro_areas = [
('Tokyo', 'JP', 36.933, (35.689722, 139.691667)),
# 其他城市
]
for name, _, _, (lat, lon) in metro_areas:
print(f'{name} - Latitude: {lat}, Longitude: {lon}')
5 序列模式匹配
Python 3.10引入了模式匹配,允许使用match/case语句对序列进行结构化匹配。例如:
# 示例:匹配嵌套序列
def match_sequence(data):
match data:
case [1, 2, *rest]:
return f"Starts with 1, 2 and the rest is {rest}"
case _:
return "No match"
print(match_sequence([1, 2, 3, 4])) # 输出: Starts with 1, 2 and the rest is [3, 4]
6 切片
6.1 为什么切片和区间排除最后一项
- 在仅指定停止位置时,容易判断切片或区间的长度。例如,
range(3)
和my_list[:3]
都只产生 3 项 - 同时指定起始和停止位置时,容易计算切片或区间的长度,做个减法即可:
stop - start
。 - 方便在索引
x
处把一个序列拆分成两部分而不产生重叠,直接使用my_list[:x]
和my_list[x:]
即可。
6.2 切片对象
切片操作使得子序列提取更加简单直观
# 示例:切片和切片赋值
nums = list(range(10))
print(nums[2:8:2]) # 输出: [2, 4, 6]
nums[2:5] = [20, 30]
print(nums) # 输出: [0, 1, 20, 30, 5, 6, 7, 8, 9]
6.3 多维切片和省略号
# 多维切片
# 创建一个3x3的二维数组
array = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
# 使用多维切片获取子数组
sub_array = array[0:2, 1:3]
# 创建一个3x3x3的三维数组
array = np.array([[[1, 2, 3], [4, 5, 6], [7, 8, 9]],
[[10, 11, 12], [13, 14, 15], [16, 17, 18]],
[[19, 20, 21], [22, 23, 24], [25, 26, 27]]])
# 使用省略号切片获取所有维度的最后一个元素
last_elements = array[..., -1]
6.4 切片赋值
切片不仅可以从序列中提取信息,还可以就地更改可变序列,即不重新构建序列。
# 创建一个列表
my_list = [1, 2, 3, 4, 5]
# 使用切片赋值替换列表的一部分
my_list[1:4] = [20, 30, 40]
print(my_list) # 输出: [1, 20, 30, 40, 5]
# 使用切片赋值插入元素
my_list[2:2] = [25, 35]
print(my_list) # 输出: [1, 20, 25, 35, 30, 40, 5]
# 使用切片赋值删除元素
my_list[3:5] = []
print(my_list) # 输出: [1, 20, 25, 40, 5]
7 使用+和*处理序列
- 拼接:使用 + 运算符将两个序列拼接成新的序列。
- 重复:使用 * 运算符将序列重复指定次数。
- 增量赋值:使用 += 和 *= 运算符修改序列。
# 拼接序列
result = a + b
# 重复序列
result = a * 3
# 增量赋值
l += [20, 30]
8 list.sort与内置函数sorted
list.sort 就地排序,返回None,而 sorted 返回一个新列表,不会修改原始序列。
# 示例:list.sort 和 sorted
data = [3, 1, 4, 1, 5]
sorted_data = sorted(data)
print(sorted_data) # 输出: [1, 1, 3, 4, 5]
data.sort(reverse=True)
print(data) # 输出: [5, 4, 3, 1, 1]
二者都接受两个可输入参数:key和reverse:
- key:为一个函数,指定排序依据;
- reverse:默认按从小到大,如果为true,代表从大到小
9 当列表不适用时
- 数组(array.array):高效地存储和处理数值列表。
- memoryview:共享内存的序列类型,可以处理数组切片;
- Numpy:用于科学计算的高级数值处理;
- 双端队列(collections.deque):快速在两端插入和删除数据;
- 集合set和frozenset:储存唯一的元素,并支持集合运算。
# 使用数组
floats = array('d', (random() for i in range(10**7)))
# 使用 memoryview
memoryview(numbers)
# 使用 NumPy
a = np.arange(12)
# 使用双端队列
dq = deque(range(10), maxlen=10)
# 使用集合
unique_elements = set(elements)
10 本章小结
本章介绍了 Python 中丰富的序列类型和它们的应用,并探讨了如何利用这些类型构建高效、简洁的代码。