Python中对象可变的(mutable)和不可变的(immutable)介绍

本文详细介绍了Python中对象的可变性和不可变性,区分了它们的区别,包括对象的标识、类型和值,以及如何在列表、字典等可变类型和整数、字符串等不可变类型中进行操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Python中对象可变的(mutable)和不可变的(immutable)介绍

Python中所有的数据都可以被视为对象。在Python中,一切皆对象,包括整数、浮点数、字符串、列表、字典等内置数据类型,也包括自定义的类和对象。

每个对象都有自己的属性和方法,可以通过点操作符来访问和调用。例如,整数对象有属性如大小、位数等,还有方法如转换为字符串、求和等。字符串对象有属性如长度、字符的索引等,还有方法如拼接、大小写转换等。

Python中的对象分为可变的(mutable)和不可变的(immutable)

Python 中每个对象(object)都有各自的标识(identity)、类型(type)和值(value)。一个对象被创建后,它的标识就绝不会改变;你可以将其理解为该对象在内存中的地址(address )。“is” 运算符可以比较两个对象的标识号是否相同;id() 函数能返回一个代表其标识的整数。

有些对象的 值 可以改变。值可以改变的对象被称为 可变的(mutable);值不可以改变的对象就被称为 不可变的(immutable)。(一个不可变容器对象如果包含对可变对象的引用,当后者的值改变时,前者的值也会改变;但是该容器仍属于不可变对象,因为它所包含的对象集是不会改变的。因此,不可变并不严格等同于值不能改变,实际含义要更微妙。) 一个对象的可变性是由其类型决定的;例如,数字、字符串和元组是不可变的,而字典和列表是可变的。https://docs.python.org/zh-cn/3/reference/datamodel.html#objects-values-and-types

可变(mutable)类型的对象可以在创建后进行修改,其值可以改变,但对象的标识(identity)保持不变。常见的可变类型包括列表(list)、字典(dict)、集合(set)等。例如,我们可以通过修改列表的元素来改变列表的值。

my_list = [1, 2, 3]

my_list[0] = 4

print(my_list)  # 输出 [4, 2, 3]

不可变(immutable)类型的对象在创建后不能被修改,任何修改操作都会返回一个新的对象。常见的不可变类型包括整数(int)、浮点数(float)、字符串(str)、元组(tuple)等。例如,我们无法直接修改字符串的某个字符,而只能通过切片和拼接等操作来创建新的字符串对象。

my_string = "Hello"

new_string = my_string + " World"

print(new_string)  # 输出 "Hello World"

需要注意的是,对象的可变性是指对象本身是否可以被修改,而不是对象所引用的变量是否可以被修改。对于不可变类型的变量,我们可以将其重新赋值为新的对象,但原始对象的值仍然保持不变。

my_string = "Hello"

my_string = my_string + " World"

print(my_string)  # 输出 "Hello World"

可变和不可变类型在内存中的存储方式和使用方法也有所不同,这会影响到对象的性能和使用方式。在编写代码时,需要根据具体需求和性能考虑使用可变或不可变类型。

特别说明

由于 Python 中的变量存放的是对象引用,所以对于不可变对象而言,尽管对象本身不可变,但变量的对象引用是可变的。运用这样的机制,有时候会让人产生糊涂,似乎可变对象变化了——变化的情况实际上是由于变量重新指向了新的对象,而不是修改了原来的对象。如下面的代码:

i = 73 

i += 2

不可变的对象的特征没有变,依然是不可变对象,变的只是创建了新对象,改变了变量的对象引用。参见下图:

​对于可变对象,其对象的内容是可以变化的。当对象的内容发生变化时,变量的对象引用是不会变化的。如下面的例子。

m=[5,9] 

m+=[6]

参见下图:

​【Mutable(可变)对象

可变对象可以在其 id() 保持固定的情况下改变其取值。

Immutable(不可变)对象

具有固定值的对象。不可变对象包括数字(numbers)、字符串(strings)和元组(tuples)。这样的对象不能被改变。如果必须存储一个不同的值,则必须创建新的对象。不可变对象不允许对自身内容进行修改。如果我们对一个不可变对象进行赋值,实际上是生成一个新对象,再让变量指向这个对象。哪怕这个对象简单到只是数字 0 和 1。】

下面程序演示了不可变参数传递的情况:

def add_fun(num):

    num +=1

    print(num, id(num))

    return

num = 3

add_fun(num)

print(num, id(num))

【顺便提示:id()的值不是固定不变的——此值系统为对象分配的内存地址,在你练习时显示的不同值是正常的。】

这段代码运行结果是:

4 1857150478736
3 1857150478704

不可变的对象的特征没有变,依然是不可变对象,变的只是创建了新对象,改变了变量的对象引用。

下面程序演示了可变参数传递的情况:

def list_append_fun(list_t):

    list_t.append("abc")

    print(list_t, id(list_t))

    return

list_t = [1,2,3]

print(list_t, id(list_t))

list_append_fun(list_t)

print(list_t, id(list_t))

这段代码运行结果是:

[1, 2, 3] 2005885237248
[1, 2, 3, 'abc'] 2005885237248
[1, 2, 3, 'abc'] 2005885237248

当传递列表或者字典时,如果改变引用的值,就改变了原始对象。对于可变对象参数,其对象的内容是可以变化的。当对象的内容发生变化时,变量的对象引用是不会变化的。

<think>好的,我现在需要回答用户关于Python可变对象不可变对象的问题。首先,我得回忆一下Python的基本数据类型,并确定它们的可变性。 我记得不可变对象包括像整数、浮点数、字符串、元组这些类型。比如,当修改字符串时,实际上是创建了一个新的字符串,原字符串不变。而可变对象比如列表、字典、集合,这些在修改时会直接改变对象本身,而不会创建新的对象。 接下来,我需要确认每个具体的数据类型是否正确分类。比如布尔类型属于不可变,因为它是基于整数的,对吧?还有frozenset应该是不可变的集合,而普通的set是可变的。这些都需要提到。 然后,用户可能需要例子来理解两者的区别。比如,列表添加元素后id不变,而字符串修改后id会变。这有助于说明可变不可变的差异。 另外,我需要解释为什么可变不可变对象在函数参数传递时表现不同,比如不可变对象作为参数时不会被函数内的操作改变,而可变对象可能会被修改。这一点对理解Python的传参机制很重要。 可能还需要提到一些注意事项,比如字典的键必须是不可变类型,所以列表不能作为键,但元组可以。这也关联到可变不可变的应用场景。 结构方面,先分类列出不可变可变对象,再举例说明,然后讨论它们的区别影响,最后总结注意事项。这样用户能逐步理解概念。 需要确保术语使用正确,比如“不可变”对应immutable,“可变”对应mutable。同时,避免使用复杂术语,保持解释清晰易懂。 可能用户会混淆某些类型,比如bytes是否不可变?是的,bytes对象也是不可变的,而bytearray是可变的。这些细节需要确认。 最后,检查是否有遗漏的类型,比如NoneType属于不可变,但用户可能更关心常见类型。所以重点放在常用的数据类型上。 总结步骤: 1. 定义不可变可变对象。 2. 列出不可变对象的具体类型。 3. 列出可变对象的具体类型。 4. 用示例说明差异。 5. 讨论对编程的影响,如参数传递、哈希等。 6. 注意事项常见误区。 现在将这些整理成结构化的回答,确保每个部分清晰,并且例子准确。</think>在Python中,对象分为**可变对象****不可变对象**,它们的核心区别在于:对象创建后是否允许修改其内部值(内存地址是否变化)。以下是具体分类说明: --- ### **一、不可变对象Immutable)** 不可变对象一旦创建,其值不可被修改。若尝试修改,Python会创建新对象并重新分配引用。 1. **常见不可变类型** - **数值类型** - 整数:`int`(如 `5`, `-3`) - 浮点数:`float`(如 `3.14`) - 复数:`complex`(如 `1+2j`) - 布尔:`bool`(本质是 `int` 的子类,`True=1`, `False=0`) - **字符串**:`str`(如 `"hello"`) - **元组**:`tuple`(如 `(1, 2, 3)`,但若元组内包含可变对象,其内容可能间接改变) - **字节串**:`bytes`(如 `b"abc"`) - **冻结集合**:`frozenset` 2. **示例说明** ```python a = 5 # 创建整数对象5,a指向内存地址A a += 1 # 创建新整数6,a改为指向地址B print(id(a)) # 地址B ≠ 地址A ``` --- ### **二、可变对象Mutable)** 可变对象允许在内存地址不变的情况下修改其内容。 1. **常见可变类型** - **列表**:`list`(如 `[1, 2, 3]`) - **字典**:`dict`(如 `{"a": 1}`) - **集合**:`set`(如 `{1, 2, 3}`) - **字节数组**:`bytearray` - **自定义类实例**(默认可变,除非显式限制) 2. **示例说明** ```python lst = [1, 2] # 内存地址C lst.append(3) # 修改lst内容,内存地址仍为C print(id(lst)) # 地址C不变 ``` --- ### **三、关键区别与影响** 1. **赋值与修改行为** - 不可变对象的赋值会复制值(或引用),修改时生成新对象。 - 可变对象的赋值传递引用,多个变量可能指向同一对象。 ```python # 不可变示例 x = "abc" y = x # xy指向同一字符串 x += "d" # x指向新字符串"abcd",y仍指向"abc" # 可变示例 a = [1, 2] b = a # ab指向同一列表 a.append(3) # ab同时变为[1, 2, 3] ``` 2. **函数参数传递** - 不可变对象作为参数时,函数内修改不会影响原对象。 - 可变对象作为参数时,函数内修改会影响原对象。 ```python def modify(num, lst): num += 1 # 不影响外部的num(不可变) lst.append(4) # 修改外部lst(可变) n = 10 my_list = [1, 2] modify(n, my_list) print(n) # 输出10(未变) print(my_list) # 输出[1, 2, 4] ``` 3. **哈希性(Hashable)** - 不可变对象可哈希(可用作字典键或集合元素)。 - 可变对象不可哈希(无法作为字典键)。 ```python d = {} d[(1, 2)] = "ok" # 元组(不可变)作为键 d[[1, 2]] = "error" # 报错!列表不可哈希 ``` --- ### **四、注意事项** 1. **元组的特殊性** 元组本身不可变,但若包含可变元素(如列表),其内部可变元素仍可修改: ```python t = ([1, 2], 3) t[0].append(3) # 允许!t变为([1, 2, 3], 3) ``` 2. **性能考量** 频繁修改不可变对象(如字符串拼接)会产生大量临时对象,此时建议使用可变类型(如列表)优化性能。 --- ### **总结** | 类型 | 不可变对象 | 可变对象 | |----------|--------------------------------|---------------------| | 典型示例 | `int`, `str`, `tuple` | `list`, `dict`, `set` | | 修改行为 | 创建新对象 | 原地修改 | | 哈希性 | 可哈希 | 不可哈希 | | 内存效率 | 修改时可能低效 | 高效 |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学习&实践爱好者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值