序列通用操作:索引与切片、拼接与重复、成员资格检查

序列这东西,说白了就是一组有序数据的集合。Python 里的字符串、列表、元组,都属于序列。它们虽然用途不同,但有一整套通用的操作——索引、切片、拼接、重复、成员检查。这些操作你天天都会用,但用对、用好,还是有不少门道的。

我记得刚入行那会儿,写爬虫解析 HTML,就因为索引搞错了一位,抓回来的数据全是乱的。排查了整整一下午。嗯,从那以后我对序列操作就格外小心了。

3.1 索引:从 0 开始,别搞错

序列里的每个元素都有个位置编号,叫索引。Python 的索引从 0 开始,不是 1。这一点,新手最容易栽跟头。

# 字符串索引
text = "Python"
print(text[0])  # 'P'
print(text[1])  # 'y'
print(text[5])  # 'n'

Python 还支持负数索引。从右往左数,-1 是最后一个元素。这个设计很巧妙,我特别喜欢用。

print(text[-1])  # 'n'
print(text[-2])  # 'o'
print(text[-6])  # 'P'
我的习惯:取最后一个元素,永远用 seq[-1],别用 seq[len(seq)-1]。又短又清晰,还不会算错。

索引越界会抛 IndexError。我曾经在生产环境里遇到过,因为列表长度动态变化,没做边界检查,直接崩了。所以,动态数据一定要先判断长度。

3.2 切片:取子序列的利器

切片语法是 seq[start:stop:step]。左闭右开——包含 start,不包含 stop。这个规则很多人记混。

nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(nums[2:5])   # [2, 3, 4]
print(nums[:4])    # [0, 1, 2, 3]
print(nums[6:])    # [6, 7, 8, 9]
print(nums[:])     # 整个列表的副本

步长 step 默认是 1。设为负数可以反向切片。这个技巧在反转序列时特别好用。

print(nums[::-1])  # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
print(nums[::2])   # [0, 2, 4, 6, 8]
print(nums[1::2])  # [1, 3, 5, 7, 9]
我曾经踩过的坑:切片不会越界。即使 stop 超出序列长度,Python 也不会报错,只是返回能取到的部分。这既是优点也是隐患——你可能会以为数据够长,结果拿到的比预期少。

切片返回的是新对象。对于列表,是浅拷贝;对于字符串,是新的字符串。记住,修改切片不会影响原序列。

3.3 拼接与重复

拼接用 +,重复用 *。这两个操作符在序列上很直观。

# 拼接
a = [1, 2, 3]
b = [4, 5, 6]
print(a + b)  # [1, 2, 3, 4, 5, 6]

# 重复
print(a * 3)  # [1, 2, 3, 1, 2, 3, 1, 2, 3]

字符串也一样:

greeting = "Hi" + " " + "there"
print(greeting)  # "Hi there"
print("Ha" * 3)  # "HaHaHa"
性能提醒:拼接大量字符串时,别用 +。每次拼接都会创建新字符串,O(n²) 的复杂度。用 ''.join(list),线性时间,快得多。我在处理日志文件时深有体会。

拼接操作要求两边类型一致。列表不能直接拼字符串,会报 TypeError。我见过有人写 [1, 2] + "3",然后一脸懵。

3.4 成员资格检查:in 与 not in

检查一个元素是否在序列里,用 in 或 not in。返回布尔值。

fruits = ["apple", "banana", "cherry"]
print("apple" in fruits)   # True
print("grape" in fruits)   # False
print("grape" not in fruits)  # True

字符串的 in 检查的是子串,不是单个字符。这个区别很重要。

text = "Hello, Python!"
print("Python" in text)  # True
print("hello" in text)   # False,大小写敏感
我常用的写法:条件判断里直接写 if item in seq:,别写 if seq.count(item) > 0:。前者清晰,后者又慢又啰嗦。

对于列表和元组,in 是线性扫描,O(n)。对于集合和字典,是哈希查找,O(1)。如果你频繁做成员检查,把列表转成集合能大幅提升性能。我在做数据去重时经常这么干。

3.5 知识结构总览

下面这张图把序列通用操作的核心逻辑串起来了。你一看就明白。

序列通用操作知识结构 序列通用操作 索引 切片 拼接与重复 成员资格检查 正向索引 负向索引 start:stop 步长 step + 拼接 * 重复 in not in 索引从 0 开始,切片左闭右开,拼接类型需一致,成员检查用 in 这些操作适用于字符串、列表、元组等所有序列类型

3.6 操作对比速查表

我把四种操作的关键点整理成了一张表。写代码时拿不准,扫一眼就行。

操作 语法 返回值 常见坑
索引 seq[i] 单个元素 越界抛 IndexError
切片 seq[start:stop:step] 新序列(浅拷贝) 左闭右开,容易多取或少取一位
拼接 seq1 + seq2 新序列 类型必须一致;大量拼接用 join
重复 seq * n 新序列 n 为 0 或负数返回空序列
成员检查 x in seq 布尔值 列表/元组是 O(n),频繁检查建议用集合
核心原则:序列操作不会修改原序列(可变序列的切片赋值除外)。所有操作都返回新对象。理解这一点,能避免很多副作用相关的 bug。

好了,序列通用操作就这些。索引和切片是基本功,拼接和重复是日常工具,成员检查是条件判断的常客。你想想看,是不是每个 Python 程序里都少不了它们?

我个人建议,把这四种操作练到肌肉记忆。写代码时不用想,手就自然敲出来了。这样你才能把注意力放在更复杂的逻辑上。