Python数据结构深入讲解:列表、字典、元组,彻底搞懂!

🌟 开场白:别让数据结构成为你的拦路虎!

各位Pythoner,大家好!我是你们的老朋友花姐💁‍♀️。

说起Python的数据结构,很多人第一反应是啥?------"这还不简单?" 是的,Python的数据结构确实比C++那种指针乱飞的世界友好太多,但是真正用起来,很多人又容易踩坑,甚至有些高级用法直接被忽略了。

比如,你知道列表(list)在Python底层是如何动态扩容的吗?

你知道字典(dict)的查找为什么那么快?

你知道元组(tuple)真的不可变吗?

今天,咱们就来一场深度解析!


一、列表(List):Python最灵活的数据结构!

1. 列表的基本用法

列表(list)是Python中最常见的数据结构,它支持存储多个元素 ,并且可以包含不同类型的数据。

1.1 创建列表

python 复制代码
# 创建一个普通列表
fruits = ["苹果", "香蕉", "橙子"]

# 创建一个空列表
empty_list = []

# 创建一个包含不同数据类型的列表
mixed_list = [1, "hello", 3.14, True]

1.2 访问和修改列表元素

python 复制代码
fruits = ["苹果", "香蕉", "橙子"]
print(fruits[0])  # 输出: 苹果
print(fruits[-1]) # 输出: 橙子(支持负索引)

fruits[1] = "梨"  # 修改元素
print(fruits)  # 输出: ['苹果', '梨', '橙子']

1.3 添加元素

python 复制代码
fruits.append("葡萄")  # 在列表末尾追加
fruits.insert(1, "西瓜")  # 在指定索引插入元素
print(fruits)

1.4 删除元素

python 复制代码
fruits.remove("橙子")  # 按值删除
del fruits[0]  # 按索引删除
print(fruits)

1.5 遍历列表

python 复制代码
for fruit in fruits:
    print(fruit)

2. 列表的底层原理:动态数组

Python的 list 其实就是动态数组 ,类似C语言的 malloc,它会预留一定的空间,存放数据。如果超出了容量,Python会自动扩容,避免频繁分配内存的性能损耗。

2.1 Python列表是如何扩容的?

python 复制代码
import sys

lst = []
print(f"初始列表大小: {sys.getsizeof(lst)} 字节")

for i in range(10):
    lst.append(i)
    print(f"添加 {i} 后,列表大小: {sys.getsizeof(lst)} 字节")

💡 总结

  • Python的 list 采用动态数组存储元素。
  • 自动扩容 机制提高性能,但如果知道最终大小,可以用 lst = [None] * n 预分配。

二、字典(Dict):Python的查找神器!

1. 字典的基本用法

字典(dict)是一种键值对(key-value)存储的数据结构,类似现实中的"联系人列表"👇:

python 复制代码
# 创建字典
person = {
    "name": "花姐",
    "age": 28,
    "city": "北京"
}

# 访问字典的值
print(person["name"])  # 输出: 花姐

# 添加/修改键值
person["gender"] = "女"
person["age"] = 29
print(person)

# 删除键值
del person["city"]
print(person)

# 遍历字典
for key, value in person.items():
    print(f"{key}: {value}")

2. 字典的查找为什么快?

字典的查询速度比列表快很多 ,因为字典是基于哈希表(Hash Table)实现的。来看下面的对比👇:

python 复制代码
import time

# 用列表查找
lst = list(range(1000000))
start = time.time()
999999 in lst  # 判断是否存在
print(f"列表查找耗时: {time.time() - start:.6f} 秒")

# 用字典查找
dct = {i: None for i in range(1000000)}
start = time.time()
999999 in dct
print(f"字典查找耗时: {time.time() - start:.6f} 秒")

📌 结果 :字典查找远快于列表,因为字典的键存储在哈希表中,查找时直接通过哈希计算位置,而列表需要逐个扫描(O(n))


三、元组(Tuple):真的不可变吗?

1. 元组的基本用法

元组(tuple)和列表很像,但它不可变,一旦创建就无法修改。

python 复制代码
# 创建元组
t = (1, 2, 3)
print(t[0])  # 访问元素

# 不能修改元素(会报错)
# t[0] = 100  #❌ TypeError: 'tuple' object does not support item assignment

# 但可以包含可变对象
t = (1, 2, [3, 4])
t[2].append(5)  # 居然成功了?
print(t)  # (1, 2, [3, 4, 5])

🚀 为什么可变了?

其实,元组本身确实是不可变的 ,但如果元组里存的是可变对象(如列表),那么这个可变对象的内容依然可以被修改

💡 总结

  • 元组的不可变性 指的是元组的结构不可变,但元组内部的可变对象仍然可以改变。
  • 如果要创建真正的不可变数据结构 ,可以使用 collections.namedtupledataclasses.dataclass(frozen=True)

2. Python中的不可变数据结构:namedtupledataclass(frozen=True)

🌟 为什么要用不可变的数据结构?

在 Python 里,默认的数据结构(如 listdict、普通的 tuple)通常是可变的,这意味着我们可以在程序运行时随意更改它们的内容。但在某些场景下,我们希望数据是只读的,比如:

  • 保护数据不被意外修改,防止 bug。
  • 让代码更具有可读性,明确表明某些数据不应更改。
  • 让数据结构可作为字典的 key(可哈希)。

2.1 namedtuple:轻量级的不可变数据结构

Python 的 collections.namedtuple 提供了一种类似 tuple 但带有字段名称 的不可变数据结构。它的底层仍然是 tuple,但可以使用字段名来访问数据,而不是只能用索引,非常方便!

2.1.1 基本使用
python 复制代码
from collections import namedtuple

# 定义一个不可变的 Point 结构体
Point = namedtuple("Point", ["x", "y"])

# 创建 Point 实例
p = Point(3, 5)

print(p.x)  # 3
print(p.y)  # 5
print(p)    # Point(x=3, y=5)

💡 为什么比普通 tuple 好?

python 复制代码
# 使用普通的 tuple
p1 = (3, 5)
print(p1[0])  # 3
print(p1[1])  # 5

# 用索引访问,不直观,容易出错

namedtuple 的最大优势就是可读性更好 !比起 tuple[0]point.x 一眼就能看懂它是什么数据。


2.1.2 namedtuple 是不可变的

既然是不可变数据结构,那么修改字段会报错👇:

python 复制代码
p.x = 10  # ❌ AttributeError: can't set attribute

如果你想要一个类似的结构,但允许修改,可以使用 dataclass(后面会讲)。


2.1.3 namedtuple 的额外功能
2.1.3.1 ._asdict():转换为字典
python 复制代码
p = Point(3, 5)
print(p._asdict())  
# 输出:{'x': 3, 'y': 5}
2.1.3.2 ._replace():创建新实例
python 复制代码
new_p = p._replace(x=10)
print(new_p)  # Point(x=10, y=5)

注意:_replace() 不会修改原来的对象,而是返回一个新的对象。

2.1.3.3 额外默认值
python 复制代码
Person = namedtuple("Person", ["name", "age", "city"])
p = Person._make(["花姐", 28, "北京"])
print(p)  # Person(name='花姐', age=28, city='北京')

2.2 dataclass(frozen=True):更强大的不可变数据结构

namedtuple 已经很不错了,但它还是有一些缺点,比如:

  • 不能设置默认值。
  • 不能使用类型注解。
  • 不能定义方法。

为了解决这些问题,Python 3.7 引入了 dataclasses,其中 frozen=True 可以创建真正不可变的数据结构!


2.2.1 基本使用
python 复制代码
from dataclasses import dataclass

@dataclass(frozen=True)  # 冻结 dataclass,使其不可变
class Point:
    x: int
    y: int

p = Point(3, 5)
print(p.x)  # 3
print(p.y)  # 5

namedtuple 一样,dataclass(frozen=True) 也会阻止修改:

python 复制代码
p.x = 10  # ❌ dataclasses.FrozenInstanceError: cannot assign to field 'x'

⚠️ 注意 :不同于 namedtupledataclass(frozen=True) 底层是 __slots__ + __dict__,而不是 tuple


2.2.2 dataclass(frozen=True) 的优势
✅ 支持默认值
python 复制代码
@dataclass(frozen=True)
class Person:
    name: str
    age: int = 18  # 默认值

p = Person("花姐")
print(p)  # Person(name='花姐', age=18)

相比 namedtupledataclass 可以直接给 age 赋默认值,而 namedtuple 需要用 _replace() 这种不太优雅的方法。


✅ 支持方法

namedtuple 只能存储数据,而 dataclass 还能添加方法👇:

python 复制代码
@dataclass(frozen=True)
class Rectangle:
    width: int
    height: int

    def area(self) -> int:
        return self.width * self.height

r = Rectangle(10, 5)
print(r.area())  # 50

✅ 自动实现 __eq____repr__
python 复制代码
@dataclass(frozen=True)  # 冻结 dataclass,使其不可变
class Point:
    x: int
    y: int
p1 = Point(3, 5)
p2 = Point(3, 5)

print(p1 == p2)  # ✅ True,dataclass 自动实现 __eq__
print(p1)        # ✅ Point(x=3, y=5),自动实现 __repr__

namedtuple 也可以自动实现 __eq__,但 dataclass 允许更多定制,比如:

python 复制代码
@dataclass(frozen=True)
class Point:
    x: int
    y: int

    def __repr__(self):
        return f"坐标({self.x}, {self.y})"

p = Point(3, 5)
print(p)  # 坐标(3, 5)

2.3 namedtuple vs. dataclass(frozen=True):如何选择?

特性 namedtuple dataclass(frozen=True)
不可变
底层结构 tuple __slots__ + __dict__
支持字段默认值
支持类型注解
支持方法
可读性 普通 更优

👉 什么时候用 namedtuple

  • 仅仅是存储少量数据,不需要方法和默认值。
  • 需要快速创建轻量级不可变对象。

👉 什么时候用 dataclass(frozen=True)

  • 需要方法、类型注解、默认值。
  • 需要更好的可读性和可维护性。

🎉 总结:牢记这些关键点!

今天,我们深度解析了Python的三大数据结构:列表、字典、元组 ,并且剖析了它们的底层原理。最重要的几个要点: ✅ 列表(list)可变 、支持动态扩容,适用于存储有序数据。

字典(dict)键值对存储 、查找快,适用于存储映射关系。

元组(tuple)不可变(但内部可变对象仍然可变),适用于存储固定数据。

希望今天的讲解能让你更深入理解Python的数据结构!💪

顺手点赞+在看就是对花姐最大的支持! ❤️🎉

相关推荐
恶霸不委屈10 分钟前
情感科技新纪元!基于DeepSeek的智能情绪价值引擎设计与实践!!!
人工智能·python·科技·deepseek
橘猫云计算机设计11 分钟前
基于django云平台的求职智能分析系统(源码+lw+部署文档+讲解),源码可白嫖!
数据库·spring boot·后端·python·django·毕业设计
码农小站14 分钟前
MyBatis-Plus 表字段策略详解:@TableField(updateStrategy) 的配置与使用指南
java·后端
技术小丁16 分钟前
使用PHP将PDF转换为图片(windows + PHP + ImageMagick)
后端
李憨憨20 分钟前
深入探究MapStruct:高效Java Bean映射工具的全方位解析
java·后端
仰望星空的打工人20 分钟前
若依改用EasyCaptcha验证码
后端
雷渊20 分钟前
通俗易懂的来解释倒排索引
java·后端·面试
知其然亦知其所以然20 分钟前
面试官狂喜!我用这 5 分钟讲清了 ThreadPoolExecutor 饱和策略,逆袭上岸
java·后端·面试
独立开阀者_FwtCoder23 分钟前
分享 8 个 丰富的 Nextjs 模板网站
前端·javascript·后端
梦兮林夕24 分钟前
06 文件上传从入门到实战:基于Gin的服务端实现(一)
后端·go·gin