开篇
在Python编程里,绝大多数业务场景都需要批量存储、处理一组数据。Python内置了四种最核心的容器类型:list(列表)、tuple(元组)、set(集合)、dict(字典)。
很多初学者刚接触时很容易混淆:它们是否有序、能不能修改、元素是否允许重复,以及什么时候该选用哪一种容器。本文结合交互式示例,从定义、常用操作、底层特性、经典坑点逐一讲解,文末附带对比表、练习题与总结,帮你彻底搞懂四大容器。
一句话快速总览:
list列表:有序可变,元素允许重复,通用动态数组tuple元组:有序不可变,元素允许重复,适合存放固定数据set集合:无序可变,元素不可重复,主打去重与集合运算dict字典:键值对映射,3.7+保留插入顺序,key不可重复,哈希表高速查找
补充:Python3.7及以上版本字典会保留插入顺序,但本质是哈希映射结构,并非序列。
1. list 列表(动态数组)
1.1 定义与特点
Python内置的一种数据类型是列表:list。list是有序的可变集合,可以随时添加和删除其中的元素。
比如,列出班里所有同学的名字,就可以用一个list表示:
>>> classmates = ['Michael', 'Bob', 'Tracy'] >>> classmates ['Michael', 'Bob', 'Tracy']变量classmates就是一个list。用len()函数可以获得list元素的个数:
>>> len(classmates) 3用索引来访问list中每一个位置的元素,索引从0开始:
>>> classmates[0] 'Michael' >>> classmates[1] 'Bob' >>> classmates[2] 'Tracy' >>> classmates[3] Traceback (most recent call last): File "<stdin>", line 1, in <module> IndexError: list index out of range当索引超出范围,Python抛出IndexError索引越界。记住:最后一个元素索引 =len(classmates) - 1。
如果要取最后一个元素,可以用-1做索引,直接获取末尾元素:
>>> classmates[-1] 'Tracy'以此类推,-2倒数第2、-3倒数第3:
>>> classmates[-2] 'Bob' >>> classmates[-3] 'Michael' >>> classmates[-4] Traceback (most recent call last): File "<stdin>", line 1, in <module> IndexError: list index out of range倒数第4个索引越界,同样报错。
1.2 常用操作
list是可变有序表,支持增、删、改:
# 在末尾追加元素 append() >>> classmates.append('Adam') >>> classmates ['Michael', 'Bob', 'Tracy', 'Adam'] # 在指定索引位置插入 insert(索引,元素) >>> classmates.insert(1, 'Jack') >>> classmates ['Michael', 'Jack', 'Bob', 'Tracy', 'Adam'] # 删除末尾元素 pop(),返回被删除的值 >>> classmates.pop() 'Adam' >>> classmates ['Michael', 'Jack', 'Bob', 'Tracy'] # 删除指定索引的元素 pop(i) >>> classmates.pop(1) 'Jack' >>> classmates ['Michael', 'Bob', 'Tracy'] # 修改指定索引元素,直接赋值 >>> classmates[1] = 'Sarah' >>> classmates ['Michael', 'Sarah', 'Tracy']list里面元素数据类型可以混合:
>>> L = ['Apple', 123, True]list元素还可以是另一个list(嵌套列表,二维数组):
>>> s = ['python', 'java', ['asp', 'php'], 'scheme'] >>> len(s) 4注意:s一共只有4个元素,s[2]本身又是一个list。拆开写更直观:
>>> p = ['asp', 'php'] >>> s = ['python', 'java', p, 'scheme']想要拿到php,可以写p[1]或者s[2][1]。理论上可以构造三维、四维数组,但日常开发极少使用。
没有任何元素的list是空列表,长度为0:
>>> L = [] >>> len(L) 01.3 优缺点 & 使用场景
优点:灵活,支持增删改查,有序可索引,使用简单 缺点:in成员判断需要遍历,查找速度慢;中间位置插入/删除会移动大量元素,性能差
适用场景:需要保存有序数据、需要频繁增删元素。
⚠️ 经典坑:列表是引用传递,浅拷贝容易踩坑
a = [1,2,3] b = a b.append(4) print(a) # a也被修改,a、b指向同一个列表对象2. tuple 元组
2.1 定义与特点
另一种有序列表叫元组:tuple。tuple和list非常类似,但是tuple一旦初始化就不能修改。
同样存储同学名字示例:
>>> classmates = ('Michael', 'Bob', 'Tracy')定义完成后,classmates这个tuple就无法变更。它没有append()、insert()这类增删方法。读取元素方式和list完全一致,支持classmates[0]、classmates[-1],但不能对索引位置重新赋值。
不可变tuple的意义:tuple不可变,代码更安全。如果确定数据不会改动,优先用tuple代替list。
2.2 定义元组的陷阱
定义tuple时,元素在定义阶段就必须确定。
>>> t = (1, 2) >>> t (1, 2)空元组直接写一对括号():
>>> t = () >>> t ()⚠️ 重点坑点:单元素元组必须加逗号
>>> t = (1) >>> t 1上面代码定义的不是元组,只是数字1。因为括号()既可以代表元组,也代表数学小括号,Python优先解释为数学表达式。单元素元组末尾必须添加逗号,,消除歧义:
>>> t = (1,) >>> t (1,)Python打印单元素元组时,也会自动带上逗号,防止和普通数值混淆。
2.3 “看起来可变”的tuple
>>> t = ('a', 'b', ['A', 'B']) >>> t[2][0] = 'X' >>> t[2][1] = 'Y' >>> t ('a', 'b', ['X', 'Y'])tuple定义时有3个元素:'a'、'b'、一个list。不是说tuple不可变吗?
核心理解:tuple所谓不可变,指元素的引用指向不可变。 tuple指向'a',就不能改成指向别的对象;指向list,就不能改成指向其他对象。但是list本身是可变对象,list内部元素可以修改!表面上看元组内容变了,但元组存储的引用地址完全没变。
如果想要完全不可变的tuple,必须保证元组内每一个元素本身都是不可变对象。
2.4 优缺点 & 使用场景
优点:不可变,数据安全;可哈希,可作为dict的key;内存占用比list更小
缺点:创建完成后不能修改
使用场景:固定不变的数据、函数多返回值、作为字典key。
配套练习 请用索引取出下面list的指定元素:
L = [ ['Apple', 'Google', 'Microsoft'], ['Java', 'Python', 'Ruby', 'PHP'], ['Adam', 'Bart', 'Bob'] ] # 打印Apple: print(L[0][0]) # 打印Python: print(L[1][1]) # 打印Bob: print(L[2][2])请问以下变量哪些是tuple类型:
a = () # tuple b = (1) # int c = [2] # list d = (3,) # tuple e = (4,5,6) # tuplelist & tuple小结 list 和 tuple 是Python内置有序集合,list可变,tuple不可变,根据业务需求选择。
3. dict 字典(键值对映射)
3.1 定义与特点
Python内置字典dict,全称dictionary,其他语言常叫map。使用**key-value(键值对)**存储,查找速度极快。
场景举例:根据名字查找成绩。如果用两个list实现:
names = ['Michael', 'Bob', 'Tracy'] scores = [95, 75, 85]给定名字查成绩,需要遍历names找到下标,再取scores的值,list越长,速度越慢。
用dict只需要一张名字-成绩映射表,无论数据量多大,查找速度几乎不变:
>>> d = {'Michael': 95, 'Bob': 75, 'Tracy': 85} >>> d['Michael'] 95dict高速查找原理
原理类似查纸质字典:
- list查找:从头到尾一页一页翻,数据越多越慢
- dict查找:通过哈希算法直接计算key对应的内存地址,一步定位,速度几乎不受数据量影响
dict是用空间换时间的数据结构。存入数据时,根据key计算存储位置;读取时,同样通过key直接定位value。
3.2 常用操作
# 添加键值对,直接赋值 >>> d['Adam'] = 67 >>> d['Adam'] 67同一个key多次赋值,后面的值会覆盖旧值:
>>> d['Jack'] = 90 >>> d['Jack'] 90 >>> d['Jack'] = 88 >>> d['Jack'] 88访问不存在的key,会抛出KeyError:
>>> d['Thomas'] Traceback (most recent call last): File "<stdin>", line 1, in <module> KeyError: 'Thomas'两种方案避免报错:
in判断key是否存在
>>> 'Thomas' in d Falseget()方法,key不存在默认返回None,可自定义默认返回值
>>> d.get('Thomas') >>> d.get('Thomas', -1) -1提示:Python交互环境中,返回
None不会显示任何输出。
删除key(连同value一起删除),使用pop(key),返回被删除value:
>>> d.pop('Bob') 75 >>> d {'Michael': 95, 'Tracy': 85}注意:Python3.7之前字典不保证顺序;Python3.7+字典会保留key插入顺序。
3.3 dict vs list 对比
dict
- 查找、插入速度极快,性能不随元素数量衰减
- 内存占用高,空间换时间
list
- 查找、插入耗时随元素变多而增加
- 内存占用更小
3.4 重要规则:key必须是不可变对象
dict依靠哈希算法计算key的存储位置。如果key可变,哈希值会变化,哈希表直接失效。 字符串、整数、tuple都是不可变对象,可以作为key;list是可变对象,不能作为dict的key。
>>> key = [1, 2, 3] >>> d[key] = 'a list' Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unhashable type: 'list'3.5 优缺点 & 使用场景
优点:按键查找O(1)速度;键值对应关系直观清晰
❌ 缺点:内存开销大;key必须是可哈希(不可变)对象,key不能重复
👉 使用场景:一对一映射关系,用户信息、词频统计、映射查表。
4. set 集合
4.1 定义与特点
set底层原理和dict很像,只存储key,不存储value。由于key不能重复,集合自带自动去重特性。
两种创建方式:
>>> s = {1, 2, 3} >>> s {1, 2, 3} # 传入list构造set >>> s = set([1, 2, 3]) >>> s {1, 2, 3}注意:打印集合展示的顺序不代表集合有序。
重复元素放入set会自动过滤:
>>> s = {1, 1, 2, 2, 3, 3} >>> s {1, 2, 3}4.2 常用操作
# add 添加元素,重复添加无效果 >>> s.add(4) >>> s {1, 2, 3, 4} >>> s.add(4) >>> s {1, 2, 3, 4} # remove 删除指定元素 >>> s.remove(4) >>> s {1, 2, 3}set支持数学集合运算:交集、并集
>>> s1 = {1, 2, 3} >>> s2 = {2, 3, 4} >>> s1 & s2 # 交集,两个集合共有的元素 {2, 3} >>> s1 | s2 # 并集,两个集合全部元素,自动去重 {1, 2, 3, 4}set和dict底层原理一致,不能放入可变对象(list/dict),可变对象无法哈希,不能存入集合。
4.3 优缺点 & 使用场景
✅ 优点:自动去重;x in set成员判断速度极快
❌ 缺点:无序,不支持下标索引;元素必须可哈希,不能重复
👉 使用场景:列表去重、求两个数据集的交集差集、快速成员判断。
小提示:
list(set(原始列表))可以快速去重;但旧版本Python会丢失顺序,需要保序场景不推荐直接使用。
5. 再议不可变对象
str字符串是不可变对象,list是可变对象。
可变对象list,调用方法会直接修改原对象本身:
>>> a = ['c', 'b', 'a'] >>> a.sort() >>> a ['a', 'b', 'c']不可变对象字符串,调用方法不会修改原对象,只会返回新对象:
>>> a = 'abc' >>> a.replace('a', 'A') 'Abc' >>> a 'abc'拆开看更清晰:
>>> a = 'abc' >>> b = a.replace('a', 'A') >>> b 'Abc' >>> a 'abc'核心理解:变量a只是一个标签,指向内存里的字符串对象'abc'。
┌───┐ ┌───────┐ │ a │────▶│ 'abc' │ └───┘ └───────┘replace()作用在'abc'字符串对象上,不会修改原字符串,而是生成全新字符串'Abc'返回。变量b指向新对象,a仍然指向原来的字符串。
┌───┐ ┌───────┐ │ a │────▶│ 'abc' │ └───┘ └───────┘ ┌───┐ ┌───────┐ │ b │────▶│ 'Abc' │ └───┘ └───────┘核心结论:调用不可变对象的方法,永远不会修改原对象,只会返回新对象,保证不可变对象恒定不变。
6. 四大容器对比总表
| 类型 | 符号 | 是否有序 | 是否可变 | 元素允许重复 | 底层结构 | 核心用途 |
|---|---|---|---|---|---|---|
| list | [] | ✅有序 | ✅可变 | ✅允许 | 动态数组 | 顺序存储,频繁增删 |
| tuple | () | ✅有序 | ❌不可变 | ✅允许 | 常量数组 | 固定数据,安全,可哈希 |
| set | {} | ❌无序 | ✅可变 | ❌不允许 | 哈希表 | 去重、集合运算 |
| dict | {k:v} | 3.7+保序 | ✅可变 | key不可重复 | 哈希表 | 键值映射,高速查找 |
补充概念:可哈希(hashable):int、str、tuple;不可哈希:list、set、dict。不可哈希对象不能作为set元素,也不能作为dict的key。
7. 综合实战小案例
需求:统计一句话中每个单词出现次数
text = "hello python hello world python python" word_list = text.split() count_dict = {} for word in word_list: if word in count_dict: count_dict[word] += 1 else: count_dict[word] = 1 print(count_dict) # 输出:{'hello': 2, 'python': 3, 'world': 1} # 获取全部不重复单词 unique_words = set(word_list) print(unique_words)结尾总结
Python四大内置容器是编程基础中的重中之重,选型核心判断逻辑:
- 需要有序存储、频繁增删元素 →list
- 数据固定,不希望被意外修改,需要可哈希特性 →tuple
- 需要自动去重、求交集/并集,快速判断元素是否存在 →set
- 需要一对一映射关系,按键高速查找 →dict
容器选型的核心原则:根据数据是否有序、是否允许重复、是否需要映射关系做选择。同时一定要理解可变对象、不可变对象与哈希的概念,这是用好set和dict的关键,也是Python面试高频考点。