Python中有可变对象和不可变对象之分。可变对象建立后可改变但地址不会改变,即变量指向的仍是原来的变量;不可变对象建立以后便不能改变,若是改变则会指向一个新的对象。python
Python中dict、list是可变对象,str、int、tuple、float是不可变对象。缓存
本文只介绍list和str,其余的同理。bash
来看一个字符串的例子:微信
>>> 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会建立一个新的字符串对象:app
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指向了不一样的对象。ui
切片操做是浅拷贝。而普通的赋值只是复制对象的索引(对象标识符、内存地址)给等号左边的变量。spa
下面来看个有趣的例子:code
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
微信公众号:小鑫的代码平常