Python的可变对象和不可变对象
2024-03-19 09:50阅读:
作者: Sam(甄峰) sam_code@hotmail.com
0. 概念:
在Python中,对象可以分为两种主要类型:可变对象和不可变对象。
可变对象:其值可以在创建后修改,包括List,dict和set.
不可变对象:其值在创建后不能被修改,包括int, float,
string,bool,tuple.
这里大家就会很疑惑,平时在编程时,int,float等修改不是很常见么?这就涉及到Python对象的三个属性:
地址,类型,值。
可变对象: 当使用 =
尝试修改其内容时,对象的值发生了改变,类型和地址并没有变化。即修改的val放到此对象的原来的内存地址了。
不可变对象:当尝试使用 =
修改其内容时,内存地址发生改变,即把对象的指向另一块内存地址,这块地址的内容是咱们所希望的。看起来也达到了目标。
所以不可变对象并非指不能使用 = 来修改其引用。而是指不能在此地址修改内容。例如:
str_1 = 'test string'
str_1[0] = 'T' 这里会导致错误,因为尝试修改其内容。
TypeError: 'str' object does not support item assignment
例1:修改变量值,查看地址是否变化:
#不可变类型
string_test1 = 'Hello World!'
iVal_test1 = 1
tuple_test = (2, 3, 4, 5)
#可变类型
List_test = [1, 2, 3, 4]
print('string_test1:', string_test1, 'id:',
id(string_test1))
print('iVal_test1:',iVal_test1, 'id:',
id(iVal_test1))
print('tuple_test:',tuple_test, 'id:',
id(tuple_test))
print('List_test:',List_test, 'id:',
id(List_test))
string_test1 = 'SpaceX'
iVal_test1 = 2
List_test[0] = 0
tuple_test = (9, 8, 7, 6)
print('After string_test1:', string_test1,
'id:', id(string_test1))
print('After iVal_test1:',iVal_test1, 'id:',
id(iVal_test1))
print('After tuple_test:',tuple_test, 'id:',
id(tuple_test))
print('After List_test:',List_test, 'id:',
id(List_test))
string_test1: Hello World! id: 2411681403952
iVal_test1: 1 id: 2411677679856
tuple_test: (2, 3, 4, 5) id: 2411679470976
List_test: [1, 2, 3, 4] id:
2411681408448
After string_test1: SpaceX id: 2411681404784
After iVal_test1: 2 id: 2411677679888
After tuple_test: (9, 8, 7, 6) id: 2411679471136
After List_test: [0, 2, 3, 4] id:
2411681408448
可以看到,尝试修改后,只有可变类型的list地址没有修改。不可变类型修改内容后,位置发生了变化。
例2:两个变量指向同一个地址:
#两个变量指向同一个地址
iVal_a = 3
iVal_b = iVal_a
print('iVal_a:', iVal_a, 'id:',
id(iVal_a))
print('iVal_b:', iVal_b, 'id:',
id(iVal_b))
list_test_a = [1, 2, 3, 4]
list_test_b = list_test_a
print('list_test_a:', list_test_a, 'id:',
id(list_test_a))
print('list_test_b:', list_test_b, 'id:',
id(list_test_b))
iVal_a = 4
print('After iVal_a:', iVal_a, 'id:',
id(iVal_a))
print('After iVal_b:', iVal_b, 'id:',
id(iVal_b))
list_test_a[-1] = 100
print('After list_test_a:', list_test_a,
'id:', id(list_test_a))
print('After list_test_b:', list_test_b,
'id:', id(list_test_b))
iVal_a: 3 id: 2752586449200
iVal_b: 3 id: 2752586449200
list_test_a: [1, 2, 3, 4] id: 2752594502976
list_test_b: [1, 2, 3, 4] id: 2752594502976
After iVal_a: 4 id: 2752586449232
After iVal_b: 3 id: 2752586449200
After list_test_a: [1, 2, 3, 100] id: 2752594502976
After list_test_b: [1, 2, 3, 100] id: 2752594502976
不可变object内容变化后,地址变了。但iVal_b因为还继续指向之前的地址,所以值没有变化。
可变object val变化后,地址没变,另一个指向此地址的object的值也跟着变了(因为地址是同一份)
这里还有个概念要注意:对象内存复用。
Python会把一些Object的内存进行复用。
Python中对不可变对象的缓存是一种性能优化机制。由于不可变对象的值不会更改,Python可以在内存中缓存相同的对象,以减少内存占用和提高性能。
这意味着如果创建多个相同值的不可变对象,它们实际上可能会引用相同的对象。
例3:
#test id 重用内存:
#test id 重用内存:
iVal_a = 12
iVal_b = 12
fVal_c = 18.1234
fVal_d = 18.1234
print('iVal_a:', iVal_a, 'id:', id(iVal_a),
'iVal_b:', iVal_b, 'id:', id(iVal_b), 'id(12):',
id(12))
print('fVal_c', fVal_c, id(fVal_c), 'fVal_d',
fVal_d, 'id:', id(fVal_d), 'id(18.1234)', id(18.1234))
iVal_a = 2
fVal_c = 11.1231
print('After ', 'iVal_a:', iVal_a, 'id:',
id(iVal_a), 'iVal_b:', iVal_b, 'id:', id(iVal_b), 'id(12):',
id(12))
print('After ', 'fVal_c', fVal_c, id(fVal_c),
'fVal_d', fVal_d, 'id:', id(fVal_d), 'id(18.1234)',
id(18.1234))
iVal_a: 12 id: 2063254159952 iVal_b: 12 id: 2063254159952
id(12): 2063254159952
fVal_c 18.1234 2063255187600 fVal_d 18.1234 id: 2063255187600
id(18.1234) 2063255187600
After iVal_a: 2 id: 2063254159632 iVal_b: 12 id:
2063254159952 id(12): 2063254159952
After fVal_c 11.1231 2063255187824 fVal_d 18.1234 id:
2063255187600 id(18.1234) 2063255187600
可以看到,两个不可变对象若值相等,则它们指向的地址与这个值的常量相同。
1.
可变对象和不可变对象作为参数使用时的影响:
在Python中,参数传递方式与对象的可变性有关。
不可变对象的参数传递,可以认为是C/C++中的值传递。在函数内,为实参创建了一个副本,即改变函数内的参数,不会改变原始对象的值。
可变对象的参数传递,可以认为是C++的引用传递。函数内对参数的修改会改变原始对象的值。