作者: 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++的引用传递。函数内对参数的修改会改变原始对象的值。
Python的可变对象和不可变对象
作者: 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++的引用传递。函数内对参数的修改会改变原始对象的值。