Python札记3:可变对象和不可变对象
Posted xietx1995
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python札记3:可变对象和不可变对象相关的知识,希望对你有一定的参考价值。
Python中有可变对象和不可变对象之分。可变对象创建后可改变但地址不会改变,即变量指向的还是原来的变量;不可变对象创建之后便不能改变,如果改变则会指向一个新的对象。
Python中dict、list是可变对象,str、int、tuple、float是不可变对象。
本文只介绍list和str,其他的同理。
字符串
来看一个字符串的例子:
>>> a = "hello"
>>> a[0] = 'a'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
上面提示,字符串类型是不支持元素赋值的,也就是说字符串是不可变对象。而当我们对字符串拼接的时候,Python会创建一个新的字符串对象:
a = "hello"
print(id(a)) # 2240543256736
a = a + " world"
print(id(a)) # 2240542770544
执行a = a + " world"
时,先计算等号右边的表达式,生成一个新的对象赋值到变量a,因此a指向的对象发生了改变,id(a)
的值也与原先不同。
列表
再来看一个列表的例子:
a = [1, 2, 3]
print(id(a)) # 2240541319816
a[0] = 5
print(id(a)) # 2240541319816
a.append(6)
print(id(a)) # 2240541319816
b = a
print(id(b)) # 2240541319816
c = b[:]
print(id(c)) # 2240541319880
c[3]=0
print(a) # [5, 2, 3, 6]
print(b) # [5, 2, 3, 6]
print(c) # [5, 2, 3, 0]
我们可以看到,当更改列表元素的值、追加元素等,列表始终指向同一个对象。我们把a赋给b以后,b也指向了同一个对象。而当把b的切片赋给c时,改变c之后,a和b未受影响,说明c指向了不同的对象。
切片操作是浅拷贝。而普通的赋值只是复制对象的索引(对象标识符、内存地址)给等号左边的变量。
函数默认值中的对象
下面来看个有趣的例子:
class Group(object):
def __init__(self, group_id, members=[]):
self.group_id = group_id
self.members = members
def add_member(self, member):
self.members.append(member)
group1 = Group(1)
group1.add_member('Zhang')
group1.add_member('Li')
print(id(group1)) # 139975434248880
print(group1.members) # ['Zhang', 'Li']
group2 = Group(2)
group2.add_member('Wang')
group2.add_member('Chen')
print(id(group2)) # 139975434248992
print(group2.members) # ['Zhang', 'Li', 'Wang', 'Chen']
我们可以看到,虽然group1和group2是不同的对象,但是group2中的members列表,group1中的members列表也变了。难道他们是同一个列表?我们来验证一下:
print(id(group1.members)) # 139662719359368
print(id(group2.members)) # 139662719359368
果然是同一个列表,原因是__init__
函数的第二个参数是默认参数,默认参数的默认值在函数创建的时候就生成了,每次调用都是用了这个对象的缓存。
所以,group1.members
和group2.members
指向了同一个对象,对group2.members
的修改也会影响group1.members
。
那么如何解决这个问题呢?方法很简单,我们将默认值设为None即可:
class Group(object):
def __init__(self, group_id, members=None):
self.group_id = group_id
if members is None:
self.members = []
def add_member(self, member):
self.members.append(member)
group1 = Group(1)
group1.add_member('Zhang')
group1.add_member('Li')
print(id(group1)) # 139879060173432
print(group1.members) # ['Zhang', 'Li']
group2 = Group(2)
group2.add_member('Wang')
group2.add_member('Chen')
print(id(group2)) # 139879060173544
print(group2.members) # ['Wang', 'Chen']
print(id(group1.members)) # 140296455689224
print(id(group2.members)) # 140296462760328
这样对于不同的group对象,它们的members是在函数被调用时才被创建,不同的group对象中的members不再引用同一个对象,所以不会再出现更新一个group对象的members也会更新另外一个group对象的members了。
我的知乎:奔三的鑫鑫
欢迎关注微信公众号:小鑫的代码日常
欢迎加入Python学习交流群:532232743,这里有各路高手等着你~
以上是关于Python札记3:可变对象和不可变对象的主要内容,如果未能解决你的问题,请参考以下文章