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