skip to content
Logo Lostman_Wang的小站

Python数据类型-基础数据类型-序列类型 --- list, tuple, range

一、通用序列操作

  • 大多数序列类型,包括可变类型和不可变类型都支持下表中的操作。collections.abc.Sequence ABC被提供用来更容易地在自定义序列类型上正确地实现这些操作。
  • 此表按优先级升序列出了序列操作。在表格中,s 和 t 是具有相同类型的序列,n, i, j 和 k 是整数而 x 是任何满足 s 所规定的类型和值限制的任意对象。
  • in 和 not in 操作具有与比较操作相同的优先级。+ (拼接) 和 * (重复) 操作具有与对应数值运算相同的优先级。
运算结果备注
x in s若 s 中某一项等于 x 则为 True,否则为 False(1)
x not in s若 s 中某一项等于 x 则为 False,否则为 True(1)
s + t将序列 s 与 t 拼接得到新序列(6)(7)
s * nn * s将序列 s 重复 n 次后拼接成新序列(2)(7)
s[i]返回序列 s 的第 i 项,下标从 0 开始(3)
s[i:j ]返回序列 s 从索引 i 到 j(不含 j)的切片(3)(4)
s[i:j:k ]返回序列 s 从索引 i 到 j、步长为 k 的切片(3)(5)
len(s)返回序列 s 的元素个数
min(s)返回序列 s 中的最小项
max(s)返回序列 s 中的最大项
s.index(x[, i[, j]])返回 x 在 s 中首次出现的位置索引;可限定搜索范围 [i, j)(8)
s.count(x)返回 x 在序列 s 中出现的总次数

1、index(self, value, start=0, stop=sys.maxsize) → int

1.1、在序列的指定切片 [start:stop ) 范围内,从左到右查找第一次出现的元素 value,并返回其下标。若未找到,抛出 ValueError。

1.2、参数

  • value(必选):要查找的元素,与序列元素做 == 比较。
  • start(可选,默认为 0):切片起始下标(包含)。
  • stop(可选,默认为 sys.maxsize):切片结束下标(不包含)。

1.3、返回值

  • 找到时返回 int 类型的下标(相对于原序列,而非切片)。
  • 未找到时抛出 ValueError: is not in sequence。

1.4、边界/细节

  • 支持负索引;start 或 stop 越界会被自动截断。
  • 若序列中允许重复元素,只返回 最左侧 的第一次出现。
  • 时间复杂度平均为 O(n),因为需要线性扫描。
>>> from collections.abc import Sequence
>>> t = ('a', 'b', 'c', 'b')
>>> Sequence.index(t, 'b') # 等价于 t.index('b')
1
>>> t.index('b', 2) # 从下标 2 开始找
3
>>> t.index('x')
Traceback (most recent call last):
...
ValueError: 'x' is not in tuple

2、count(self, value) → int

2.1、统计序列中 所有位置(无切片参数)与 value 相等的元素出现次数。

2.2、参数

  • value(必选):要计数的元素,使用 == 比较。

2.3、返回值

  • int:出现次数;若一次也没有返回 0。
  • 不会抛出异常,即使元素不存在。

2.4、边界/细节

  • 同样使用线性扫描,时间复杂度 O(n)。
  • 对于可变序列(如 list),统计的是调用时刻的快照。
  • 字符串、元组、列表、bytes 等内部实现均遵循同一语义。
>>> seq = [1, 2, 1, 3, 1]
>>> seq.count(1)
3
>>> seq.count(99)
0
>>> 'banana'.count('na')
2

3、len()获取列表长度

# 获取列表长度
print(len([1, 2, 3, 4]))
# 输出结果
4

4、max()获取列表值最大的元素

# max
a = [1, 2, 3]
print(max(a))
# 输出结果
3

5、min()获取列表值最小的元素

二、不可变序列类型

  • 不可变序列类型普遍实现而可变序列类型未实现的唯一操作就是对hash() 内置函数的支持。
  • 这种支持允许不可变类型,例如tuple 实例被用作dict 键,以及存储在set 和frozenset 实例中。
  • 尝试对包含有不可哈希值的不可变序列进行哈希运算将会导致TypeError。

三、可变序列类型

  • 以下表格中的操作是在可变序列类型上定义的。collections.abc.MutableSequence ABC 被提供用来更容易地在自定义序列类型上正确实现这些操作。
  • 表格中的 s 是可变序列类型的实例,t 是任意可迭代对象,而 x 是符合对 s 所规定类型与值限制的任何对象 (例如,bytearray 仅接受满足 0 <= x <= 255 值限制的整数)。
运算结果备注
s[i] = x将 s 的第 i 项替换为 x
s[i:j ] = t将 s 从 i 到 j 的切片替换为可迭代对象 t 的内容
del s[i:j ]等同于 s[i:j ] = []
s[i:j:k ] = t将 s[i:j:k ] 的元素替换为 t 的元素(1)
del s[i:j:k ]从列表中移除 s[i:j:k ] 的元素
s.append(x)将 x 添加到序列的末尾等同于 s[len(s):len (s)] = [x]
s.clear()从 s 中移除所有项等同于 del s[:] (5)
s.copy()创建 s 的浅拷贝等同于 s[:] (5)
s.extend(t) 或 s += t用 t 的内容扩展 s等同于 s[len(s):len (s)] = t
s *= n使用 s 的内容重复 n 次来对其进行更新(6)
s.insert(i, x)在索引 i 处插入 x等同于 s[i:i ] = [x]
s.pop() 或 s.pop(i)移除并返回索引 i 处的元素(2)
s.remove(x)移除第一个等于 x 的元素(3)
s.reverse()就地逆序列表元素(4)

1、append(self, value) → None

  • 作用:在序列尾部追加单个元素。
  • 参数:value – 待追加的对象。
  • 返回值:None(就地修改)。
  • 复杂度:均摊 O(1)。
>>> lst = [1, 2]
>>> lst.append(3)
>>> lst
[1, 2, 3]

2、clear(self) → None

  • 作用:删除序列中所有元素,使其长度变为 0。
  • 参数:无。
  • 返回值:None。
>>> lst = [1, 2, 3]
>>> lst.clear()
>>> lst
[]

3、copy(self) → MutableSequence

  • 作用:返回序列的浅拷贝(新对象,元素本身不复制)。
  • 参数:无。
  • 返回值:与原序列同类型的空壳副本。
>>> lst = [[1], [2]]
>>> cp = lst.copy()
>>> cp[0].append(9) # 修改元素,原列表受影响
>>> lst
[[1, 9], [2]]

4、extend(self, iterable) → None

  • 作用:将可迭代对象中的所有元素依次追加到尾部。
  • 参数:iterable – 任意可迭代对象(列表、元组、生成器等)。
  • 返回值:None。
  • 复杂度:O(k),k 为 iterable 长度。
>>> lst = [1, 2]
>>> lst.extend((3, 4))
>>> lst
[1, 2, 3, 4]

5、insert(self, index, value) → None

  • 作用:在指定下标前插入元素;负索引也可。
  • 参数:index – 插入位置;value – 待插入元素。
  • 返回值:None。
  • 复杂度:O(n)。
>>> lst = ['a', 'c']
>>> lst.insert(1, 'b')
>>> lst
['a', 'b', 'c']

6、pop(self, index=-1) → element

  • 作用:移除并返回指定下标的元素;默认移除最后一个。
  • 参数:index – 可选,默认为 -1。
  • 返回值:被弹出的元素。
  • 异常:IndexError(空序列或越界)。
>>> lst = [10, 20, 30]
>>> lst.pop()
30
>>> lst.pop(0)
10
>>> lst
[20]

7、remove(self, value) → None

  • 作用:删除第一个与 value 相等的元素。
  • 参数:value – 待移除的值(按 == 比较)。
  • 返回值:None。
  • 异常:ValueError(元素不存在)。
>>> lst = [1, 2, 3, 2]
>>> lst.remove(2)
>>> lst
[1, 3, 2]

8、reverse(self) → None

  • 作用:就地反转序列顺序。
  • 参数:无。
  • 返回值:None。
  • 复杂度:O(n)。
>>> lst = [1, 2, 3]
>>> lst.reverse()
>>> lst
[3, 2, 1]

四、列表 list

  • 列表是可变序列,通常用于存放同类项目的集合(其中精确的相似程度将根据应用而变化)

1、构造方法:

1.1、字面量构造(空列表或初值列表)

q = [] # 空队列
q = [1, 2, 3] # 带初始元素
  • 最轻量,O(1) 创建;
  • 只负责“容器”,不提供线程安全或性能优化

1.2、构造函数 list(iterable)

q = list(range(5)) # [0, 1, 2, 3, 4]
q = list("abc") # ['a', 'b', 'c']
  • 任何可迭代对象 → 列表;
  • 复杂度 O(n),n 为去重后元素个数。

1.3、列表推导式 / 生成器表达式

# 列表推导式:一次性构造
q = [x**2 for x in range(10) if x % 2 == 0]
# 生成器表达式:惰性迭代,先转 list
q = list(x for x in range(1000000) if x % 997 == 0)
  • 语法灵活,可直接生成“队列初始数据”。

1.4、从其他容器转换

源容器示例备注
tuplelist((1,2,3))元组 → 列表
setlist({1,2,3})集合 → 列表(无序)
dictlist({'a':1,'b':2})键视图 → 列表
文件对象list(open('file.txt'))每行一个元素

1.5、* 展开构造

# 合并多个可迭代对象
q = [*range(3), *"abc", *[7, 8]] # [0, 1, 2, 'a', 'b', 'c', 7, 8]
  • Python 3.5+ 的解包语法,用于“一次性拼接”。

1.6、双端队列(deque)—— 官方推荐高性能队列

from collections import deque
dq = deque() # 空双端队列
dq = deque([1, 2, 3]) # 带初始数据
dq = deque(maxlen=1000) # 环形缓冲区,满时自动丢弃最旧元素
  • 生产环境 推荐 collections.deque,因为它在两端插入/删除都是 O(1),而 list.insert(0, x) 是 O(n)。
  • 完全兼容 list 的迭代、切片读取;
  • 额外方法:appendleft, popleft, extendleft, rotate, maxlen。

1.7、线程安全队列(queue 模块)

import queue
q = queue.Queue() # FIFO
q = queue.Queue(maxsize=100) # 阻塞/非阻塞模式
  • 若需要在 多线程 中当真正队列,使用 queue.Queue/LifoQueue/PriorityQueue,内部用锁保护
  • 与 list 接口不同:通过 put, get, task_done 管理。

1.8、性能对比速览

操作listdequeQueue
尾部 appendO(1)O(1)O(1)
头部 insertO(n)O(1)O(1)
线程安全
可切片
  • 临时/单线程:q = [] 或 q = list(iterable)。
  • 高频两端操作:用 collections.deque(iterable)。
  • 多线程安全:用 queue.Queue(maxsize)。

2、list.sort(*, key=None, reverse=False)

2.1、作用

  • 原地重排:调用后原列表顺序被永久改变。
  • 稳定排序:排序前后,相等元素的相对顺序不变。
  • 多字段排序:可一次指定多个键,实现多级排序(例如先按年龄再按姓名)。

2.2、参数

形参类型默认值说明
keycallable 或 NoneNone排序键函数。对每个元素调用 key(element),返回值作为排序依据。常见用法:
- key=str.lower(忽略大小写)
- key=lambda x: x.age(按对象属性)
- key=operator.itemgetter(1)(按元组第二字段)
reverseboolFalseTrue降序排序;False 为升序。

2.3、返回值

  • 始终返回 None。
  • 因为排序是原地操作,所以 不返回新列表;若写 new_list = old_list.sort(),会得到 None,这是常见错误。
# 基础升序
nums = [3, 1, 4, 1, 5]
nums.sort()
print(nums) # [1, 1, 3, 4, 5]
# 降序
nums.sort(reverse=True)
print(nums) # [5, 4, 3, 1, 1]
# 按字符串长度排序
words = ['python', 'is', 'awesome']
words.sort(key=len)
print(words) # ['is', 'python', 'awesome']
# 多字段排序:先按成绩降序,再按姓名升序
students = [('Alice', 90), ('Bob', 85), ('Alice', 95)]
students.sort(key=lambda s: (-s[1], s[0]))
print(students) # [('Alice', 95), ('Alice', 90), ('Bob', 85)]

3、公共功能

3.1、运算符 +

  • 相加,两个列表相加获取生成一个新的列表。
# + 运算
a = [1] + [2, 3, ] + [4, 5]
print(a)
b = [1, 2] + ["3", "4"]
print(b)
# 输出结果
[1, 2, 3, 4, 5]
[1, 2, '3', '4']

3.2、运算符 *

  • 相乘,列表*整型 将列表中的元素再创建N份并生成一个新的列表。
# * 运算
a = [1, 2] * 3
print(a)
# 输出结果
[1, 2, 1, 2, 1, 2]

3.3、索引(下标)取值

# 读
user_list = ["范德彪","刘华强",'尼古拉斯赵四']
print( user_list[0] )
print( user_list[2] )
print( user_list[3] ) # 报错
# 改
user_list = ["范德彪","刘华强",'尼古拉斯赵四']
user_list[0] = "刘德华"
print(user_list) # ["刘德华","刘华强",'尼古拉斯赵四']
# 删
user_list = ["范德彪","刘华强",'尼古拉斯赵四']
del user_list[1]
user_list.remove("刘华强")
ele = user_list.pop(1)

3.4、切片取值

  • 和字符串一样,列表也可以切片
  • 使用语法:列表[start : end : step],获取列表中在 [start, end) 范围的子列表
  • 注意范围 [start, end) 包含 start,不包含 end
  • step 是步长,设为 n,则每隔 n 个元素获取一次
# 读
user_list = ["范德彪","刘华强",'尼古拉斯赵四']
print( user_list[0:2] ) # ["范德彪","刘华强"]
print( user_list[1:] )
print( user_list[:-1] )

3.5、切片赋值

# 切片赋值
a = ["1", "2", "3"]
print(a)
a[:] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] # 切片获取所有元素,并重新赋值
print(a)
a[2:4] = [33, 44]
print(a)
a[2:4] = [] # 相当于去掉第 3、4 个元素
print(a)
a[:] = [] # 将 a 赋值为空列表
print(a)
# 输出结果
['1', '2', '3']
[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
[1, 2, 33, 44, 5, 6, 7, 8, 9, 0]
[1, 2, 5, 6, 7, 8, 9, 0]
[]

3.6、关键字 in

  • 通过关键字 in 检查列表中是否包含指定元素,返回 bool 值
  • not in 则是取反
# in、not in
a = [1, 2, True, {"name": "周杰伦"}, ["how", "hi"]]
print(1 in a)
print(3 in a)
print({"name": "周杰伦"} in a)
print(False not in a)
# 输出结果
True
False
True
True

4、易错点

  • 在 Python 里,一边遍历列表一边删元素是新手最容易踩的坑之一,核心问题是:列表是可变序列,删除操作会立即改变其长度和索引,从而让循环“错位”。下面用 3 个典型场景说明坑点、原因及正确做法。

4.1、漏删 / 跳过元素

lst = [1, 2, 2, 3]
for i in lst:
if i == 2:
lst.remove(i) # 期望删掉所有 2
print(lst) # 结果: [1, 2, 3] ← 还有一个 2 没删掉

原因:第一次删下标 1 的 2 后,后面的元素整体前移,原下标 2 的 2 变成了下标 1,但 for 循环的指针已经走到下标 2 了,于是被跳过。

4.2、IndexError:索引越界

lst = [0, 1, 2]
for i in range(len(lst)):
del lst[i]
# IndexError: list index out of range

原因:列表长度在循环中不断缩短,而 range(len(lst)) 一开始就把长度固定为 3,当 i=2 时列表只剩 1 个元素,导致越界。

4.3、隐藏的副作用(列表推导式同理)

lst = [1, 2, 3, 4]
new = [x for x in lst if lst.remove(x) or True] # 看似技巧,实则灾难
# new == [1, 2, 3, 4],lst 被清空了

原因list.remove 会就地修改原列表,列表推导式内部迭代器仍在同一列表上工作,行为完全不可预测。

4.4、✅ 正确姿势

方法代码示例说明
倒序遍历for i in range(len(lst)-1, -1, -1): if cond: del lst[i]删除不影响未遍历索引
列表拷贝for x in lst[:]: if cond: lst.remove(x)遍历副本,修改原列表
生成新列表lst = [x for x in lst if not cond]函数式写法,最简洁
filterlst = list(filter(lambda x: not cond, lst))与推导式等价

4.5、一句话总结

  • 不要在同一个可变列表上同时进行“遍历指针”和“长度修改”两类操作;要么倒序、要么遍历副本、要么生成新列表。

五、元组tuple

  • 元组是不可变序列,通常用于储存异构数据的多项集(例如由enumerate() 内置函数所产生的二元组)。
  • 元组也被用于需要同构数据的不可变序列的情况(例如允许存储到set 或dict 的实例)。

1、构造方法:

1.1、字面量括号法(最常用)

t1 = (1, 2, 3) # 带括号
t2 = 1, 2, 3 # 括号可省,逗号是灵魂
t3 = () # 空元组
t4 = (42,) # 单元素必须加逗号,否则变成整数
细节说明
逗号是标识符没有逗号,(42) 只是表达式分组,类型为 int
性能CPython 在编译期直接生成 BUILD_TUPLE,O(1)。

1.2、内置构造函数 tuple(iterable=())

t5 = tuple() # 空元组
t6 = tuple([1, 2, 3]) # 列表 → 元组
t7 = tuple('abc') # 字符串 → ('a', 'b', 'c')
t8 = tuple({'x': 1, 'y': 2}) # 字典 → 键视图元组
形参默认值复杂度
iterable()O(n),n 为元素个数

1.3、生成器 / 推导式转元组

t9 = tuple(i**2 for i in range(5)) # 生成器表达式
t10 = tuple([i for i in [1, 2, 2] if i % 2]) # 列表推导式
  • 生成器表达式惰性迭代,节省一次中间列表内存。

1.4、序列拆包与星号展开

a, b, *c = [1, 2, 3, 4] # c -> [3, 4]
t11 = (*range(3), *"abc") # 拆包后合并 -> (0, 1, 2, 'a', 'b', 'c')
  • Python 3.5+ 支持 * 解包语法,编译器生成 BUILD_TUPLE_UNPACK

1.5、嵌套元组(tuple 嵌套 tuple)

nested = ((1, 2), (3, 4), (5, 6))
  • 完全合法,用于 坐标点、树节点、多维索引 等场景。

1.6、命名元组(namedtuple)—— 语义元组

from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])
p = Point(1, 2) # Point(x=1, y=2)
  • 仍是 tuple 子类,但带字段名,提升可读性。

1.7、单元素陷阱与布尔陷阱

表达式结果类型说明
(42)int无逗号,只是括号分组
(42,)tuple有逗号,单元素元组
bool(())False空元组为假
bool((0,))True非空即真

1.8、性能对比

构造方式时间复杂度额外内存备注
字面量 (1, 2, 3)O(1)0字节码直接生成
tuple(iterable)O(n)O(n)需要遍历一次
生成器表达式O(n)0(惰性)无中间列表

1.9、线程安全

  • 元组 不可变,天然线程安全,可作为 dict 键或 set 元素。

2、公共功能

2.1、运算符 +

  • 使用运算符 + 连接多个元组
# +
tup1 = (1,)
tup2 = (2, 3)
print(tup1 + tup2)
# 输出结果
(1, 2, 3)

2.2、运算符 *

  • 使用运算符 * 将元组的元素重复
# *
tup = (1, 2)
print(tup * 2)
# 输出结果
(1, 2, 1, 2)

2.3、索引(下标)取值

# 索引
tup = [1, 2, 3, 4, 5]
print(tup[0])
print(tup[-1])
print(tup[2])
# 输出结果
1
5
3

2.4、切片取值

  • 和列表一样,元组也可以切片
  • 使用语法:元组[start : end : step],获取元组中在 [start, end) 范围的子元组
  • 注意范围 [start, end) 包含 start,不包含 end
  • step 是步长,设为 n,则每隔 n 个元素获取一次
# 切片
tup = [1, 2, 3, 4, 5, 6, 7, 8]
print(tup[:]) # 取全部元素
print(tup[0:]) # 取全部元素
print(tup[2:5]) # 取第 3 个元素到第 5 个元素
print(tup[::-1]) # 倒序取所有元素
print(tup[-3:-1]) # 取倒数第 3 个元素到倒数第 2 个元素
# 输出结果
[1, 2, 3, 4, 5, 6, 7, 8]
[1, 2, 3, 4, 5, 6, 7, 8]
[3, 4, 5]
[8, 7, 6, 5, 4, 3, 2, 1]
[6, 7]

2.5、关键字 in

  • 通过关键字 in 检查元组中是否包含指定元素,返回 bool 值
  • not in 则是取反
# in
tup = (1, 2, 3)
print(1 in tup)
print(22 not in tup)
# 输出结果
True
True

六、range 对象

  • range 是一个 不可变序列,用来生成 等差整数序列。它既节省内存又支持随机访问,是 for 循环索引切片 最常用的工具之一

1、构造方法

range(stop)
range(start, stop)
range(start, stop, step)
  • 仅限位置参数,无关键字参数。
  • 三种重载在 CPython 中最终都会归一到 (start, stop, step) 三元组。

2、参数语义与默认值

形参类型默认值说明
startint0序列起始值(包含)
stopint— 必填序列终止值(不包含
stepint1步长,可为负;不能为 0
  • step为负,序列 递减
  • 所有参数必须是 整数(Python 3 起拒绝 float)。

3、返回值

  • range 对象lazy):不立即生成列表,仅保存 (start, stop, step)
  • 支持 索引、切片、len()、成员检测且均为 O(1)
  • 要得到列表需显式转换:list(range(...)),复杂度 O(n)

4、行为示例

>>> list(range(5)) # 0..4
[0, 1, 2, 3, 4]
>>> list(range(2, 7)) # 2..6
[2, 3, 4, 5, 6]
>>> list(range(1, 10, 2)) # 1,3,5,7,9
[1, 3, 5, 7, 9]
>>> list(range(5, 0, -1)) # 5,4,3,2,1
[5, 4, 3, 2, 1]
>>> range(0, 5)[::-1] # 切片反转 → range(4, -1, -1)
range(4, -1, -1)

5、性能与内存

  • 存储仅需 3 个整数(start, stop, step),与长度无关。
  • len(range)计算为 max(0, (stop-start+step-1)//step),O(1)。
  • 遍历速度接近 C 级循环,比生成器更快。

6、常见误区

错误写法原因
range(5.0)TypeError: ‘float’ object cannot be interpreted as an integer
range(5, 10, 0)ValueError: step argument must not be zero
range(10)[10]IndexError: range object index out of range

7、==、!= 操作

  • 使用 == 和 != 检测 range 对象是否相等是将其作为序列来比较。也就是说,如果两个 range 对象表示相同的值序列就认为它们是相等的。(请注意比较结果相等的两个 range 对象可能会具有不同的start, stop和step 属性,例如 range(0) == range(2, 1, 3) 而 range(0, 3, 2) == range(0, 4, 2)。)