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的数据结构!💪

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

相关推荐
___波子 Pro Max.1 分钟前
GitHub Actions配置python flake8和black
python·black·flake8
君爱学习24 分钟前
RocketMQ延迟消息是如何实现的?
后端
Falling4241 分钟前
使用 CNB 构建并部署maven项目
后端
阿蒙Amon1 小时前
【Python小工具】使用 OpenCV 获取视频时长的详细指南
python·opencv·音视频
程序员小假1 小时前
我们来讲一讲 ConcurrentHashMap
后端
爱上语文1 小时前
Redis基础(5):Redis的Java客户端
java·开发语言·数据库·redis·后端
萧曵 丶1 小时前
Rust 中的返回类型
开发语言·后端·rust
橘子编程2 小时前
Python-Word文档、PPT、PDF以及Pillow处理图像详解
开发语言·python
蓝婷儿2 小时前
Python 机器学习核心入门与实战进阶 Day 2 - KNN(K-近邻算法)分类实战与调参
python·机器学习·近邻算法