Python中可变对象与不可变对象详细教程

 更新时间:2026年06月03日 09:42:34   作者:登山人在路上  
Python中,可变数据类型指的是数据值可以改变的数据类型,而不可变数据类型指的是数据值不可改变的数据类型,这篇文章主要介绍了Python中可变对象与不可变对象的相关资料,需要的朋友可以参考下

前言

我们来深入解析Python中一个核心概念:可变(Mutable)对象不可变(Immutable)对象。这个概念直接关系到你的代码在内存中如何运作,尤其是在进行赋值、拷贝、传参等操作时,理解它可以帮助你避免许多难以发现的bug。

核心定义

判断一个对象是可变的还是不可变的,标准只有一个:当对象创建之后,它里面的值能否被改变?

  1. 不可变对象:对象创建后,其内容(或状态)无法被修改。如果你尝试修改它,Python实际上会创建一个新的对象,而不是在原有对象上修改。

  2. 可变对象:对象创建后,其内容(或状态)可以被修改。你可以在原内存地址上直接修改对象的值,而不会创建新对象。

1. 常见的不可变对象

  • 整数(int)

  • 浮点数(float)

  • 布尔值(bool)

  • 字符串(str)

  • 元组(tuple) —— 注意:元组本身不可变,但如果它包含可变对象(如列表),那个内部对象是可变的。

  • 冻结集合(frozenset)

示例:看似"修改"了不可变对象

name = "hello"
print(f"原始对象的内存地址: {id(name)}")  # id() 可以查看对象的内存地址

name = name + " world"
print(f"新对象的内存地址: {id(name)}")

# 输出:
# 原始对象的内存地址: 1399876543210  (示例地址)
# 新对象的内存地址: 1399876545678  (地址变了!)

发生了什么?

当执行 name = name + " world" 时,Python并没有在原来的字符串 "hello" 后面添加内容。因为字符串是不可变的,它不能直接在原处修改。所以,Python在内存中创建了一个全新的字符串对象 "hello world",然后将变量 name 重新指向了这个新对象。原来的 "hello" 对象如果没有其他变量引用,最终会被垃圾回收。

2. 常见的可变对象

  • 列表(list)

  • 字典(dict)

  • 集合(set)

  • 自定义类的实例对象(默认情况下)

示例:修改可变对象

my_list = [1, 2, 3]
print(f"修改前列表的内存地址: {id(my_list)}")

my_list.append(4)  # 在列表末尾添加一个元素
print(f"修改后列表的内容: {my_list}")
print(f"修改后列表的内存地址: {id(my_list)}")

# 输出:
# 修改前列表的内存地址: 1399876547890  (示例地址)
# 修改后列表的内容: [1, 2, 3, 4]
# 修改后列表的内存地址: 1399876547890  (地址没变!)

示例2:

# 创建一个可变对象(列表)
a = [1, 2, 3]      # a 指向一个列表对象 [1, 2, 3]
print(f"初始状态:")
print(f"a = {a}, id(a) = {id(a)}")

b = a              # b 指向同一个列表对象
print(f"\n执行 b = a 后:")
print(f"a = {a}, id(a) = {id(a)}")
print(f"b = {b}, id(b) = {id(b)}")
print(f"a is b? {a is b}")  # True,确实是同一个对象

# 情况1:通过 a 修改列表内容(不是重新赋值)
a.append(4)        # 修改可变对象的内容
print(f"\n执行 a.append(4) 后:")
print(f"a = {a}, id(a) = {id(a)}")
print(f"b = {b}, id(b) = {id(b)}")
print(f"b 看到了变化!因为 a 和 b 指向同一个列表")

# 情况2:重新给 a 赋值(让 a 指向新对象)
a = [5, 6, 7]      # a 现在指向一个新列表
print(f"\n执行 a = [5, 6, 7] 后:")
print(f"a = {a}, id(a) = {id(a)}")
print(f"b = {b}, id(b) = {id(b)}")
print(f"b 仍然指向原来的列表,不受 a 重新赋值的影响")

输出:

初始状态:
a = [1, 2, 3], id(a) = 1399876543210

执行 b = a 后:
a = [1, 2, 3], id(a) = 1399876543210
b = [1, 2, 3], id(b) = 1399876543210
a is b? True

执行 a.append(4) 后:
a = [1, 2, 3, 4], id(a) = 1399876543210
b = [1, 2, 3, 4], id(b) = 1399876543210
b 看到了变化!因为 a 和 b 指向同一个列表

执行 a = [5, 6, 7] 后:
a = [5, 6, 7], id(a) = 1399876545678
b = [1, 2, 3, 4], id(b) = 1399876543210
b 仍然指向原来的列表,不受 a 重新赋值的影响

发生了什么?

当我们调用 my_list.append(4) 时,Python直接在原有的列表对象上进行了修改。它没有创建新的列表,只是改变了原列表的内部状态。因此,列表的内存地址在修改前后是完全相同的。

3. 为什么这个区别很重要?

理解可变与不可变,对于编写正确的代码至关重要,尤其是在以下场景:

a) 变量赋值和引用

内存示意图:

栈内存:
a: [10]  →  a: [20]  (a的值被直接修改)
b: [10]      b: [10]  (b独立存储,不受影响)

2. 对于引用类型(Reference Types)

// 假设有一个 Person 类
Person a = new Person("Alice");  // a 存储的是对象的引用(内存地址)
Person b = a;                    // 把引用(地址)复制给 b
a.setName("Bob");                 // 通过 a 修改对象内容
System.out.println(b.getName());  // 输出: "Bob" (b 指向同一个对象)

a = new Person("Charlie");        // a 指向一个新对象
System.out.println(b.getName());  // 输出: "Bob" (b 仍然指向原来的对象)

b) 函数参数的传递

Python的参数传递方式是"对象引用传递"。这意味着函数内部接收的是指向实际对象的引用。

c) 作为字典的键

4. 特殊情况:元组中包含可变对象

这是一个需要特别注意的地方。元组本身是不可变的,这意味着你不能给元组添加或删除元素,也不能替换元组中的某个元素。但是,如果元组中包含了可变对象(如列表),那么这个可变对象的内容是可以被修改的

t = (1, 2, [3, 4])  # 元组中包含一个列表
print(t)  # 输出: (1, 2, [3, 4])

# t[0] = 10      # 错误!不能替换元组的元素,会报 TypeError
# t.append(5)    # 错误!元组没有 append 方法

t[2][0] = 999   # 可以修改元组内部列表的元素
print(t)  # 输出: (1, 2, [999, 4])  (元组的内容看起来变了!)

解释:

虽然元组存储的引用本身不能变,但它并没有限制这些引用所指向的对象内部是否能变。这里,元组中第三个槽位始终指向同一个列表对象。我们只是修改了这个列表对象的内容,并没有改变元组中存储的引用。所以从元组的角度看,它依然是"不可变"的。

总结

特性不可变对象可变对象
常见类型intfloatstrtuplefrozensetlistdictset, 自定义类实例
修改操作任何修改都会创建一个新对象可以在原内存地址上直接修改内容
内存地址修改后内存地址会改变修改后内存地址通常不变
安全性多个引用共享时安全,不会被意外修改多个引用共享时需谨慎,一处修改处处可见
作为字典键可以不可以
函数传参函数内修改不会影响外部变量函数内修改会影响外部对象

理解这个核心区别,是掌握Python内存模型和写出健壮代码的关键一步。

到此这篇关于Python中可变对象与不可变对象详细教程的文章就介绍到这了,更多相关Python可变对象与不可变对象内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 在VSCode中搭建Python开发环境并进行调试

    在VSCode中搭建Python开发环境并进行调试

    这篇文章介绍了在VSCode中搭建Python开发环境并进行调试的方法,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-06-06
  • OpenCV机器学习MeanShift算法笔记分享

    OpenCV机器学习MeanShift算法笔记分享

    这篇文章主要介绍了OpenCV机器学习MeanShift算法笔记分享,有需要的朋友可以借鉴参考下,希望可以对各位读者的OpenCV算法学习能够有所帮助
    2021-09-09
  • 通俗讲解python 装饰器

    通俗讲解python 装饰器

    这篇文章主要介绍了python 装饰器的相关资料,帮助大家更好的理解和学习python装饰器的相关知识,感兴趣的朋友可以了解下
    2020-09-09
  • 关于Python 中的时间处理包datetime和arrow的方法详解

    关于Python 中的时间处理包datetime和arrow的方法详解

    这篇文章主要介绍了关于Python 中的时间处理包datetime和arrow的相关知识,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-03-03
  • Python多进程协作模拟实现流程

    Python多进程协作模拟实现流程

    当多个进程使用同一份数据资源的时候,因为进程的运行没有顺序,运行起来也无法控制,如果不加以干预,往往会引发数据安全或顺序混乱的问题,所以要在多个进程读写共享数据资源的时候加以适当的策略,来保证数据的一致性问题
    2023-01-01
  • Python Pandas数据结构简单介绍

    Python Pandas数据结构简单介绍

    这篇文章主要介绍了Python Pandas数据结构简单介绍的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-07-07
  • Python使用Arrow管理日期与时间的完整指南

    Python使用Arrow管理日期与时间的完整指南

    时间在数据分析中扮演着至关重要的角色,而选择适当的时间处理模块对于提高代码效率和可读性至关重要,本文将深入介绍 Arrow 模块,探讨其相对于其他时间处理模块的优势,以及在数据分析中的实际应用,需要的朋友可以参考下
    2025-06-06
  • Python实现一键下载并整合统计年鉴Excel文件

    Python实现一键下载并整合统计年鉴Excel文件

    这篇文章主要为大家详细介绍了如何使用Python实现一键下载并整合统计年鉴Excel文件,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下
    2026-04-04
  • Python3实现计算两个数组的交集算法示例

    Python3实现计算两个数组的交集算法示例

    这篇文章主要介绍了Python3实现计算两个数组的交集算法,结合2个实例形式总结分析了Python3针对数组的遍历、位运算以及元素的添加、删除等相关操作技巧,需要的朋友可以参考下
    2019-04-04
  • 基于pytorch的保存和加载模型参数的方法

    基于pytorch的保存和加载模型参数的方法

    今天小编就为大家分享一篇基于pytorch的保存和加载模型参数的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-08-08

最新评论