python可变对象与不可变对象

青旅半醒 2022-05-27 10:00 312阅读 0赞

转自:http://mp.weixin.qq.com/s/vj9IxEHN815eASmWlDSzMg

Python中有可变对象和不可变对象之分。可变对象创建后可改变但地址不会改变,即变量指向的还是原来的变量;不可变对象创建之后便不能改变,如果改变则会指向一个新的对象。

Python中dict、list是可变对象,str、int、tuple、float是不可变对象。

来看一个字符串的例子

  1. a = "hello"
  2. print(id(a)) # 输出 140022851974560
  3. a[0]="a" # 抛出异常:TypeError: 'str' object does not support item assignment
  4. a = a + " world"
  5. print(id(a)) # 输出 140022850763824

上面的例子里,修改a指向的对象的值会导致抛出异常。

执行 a = a + “ world”时,先计算等号右边的表达式,生成一个新的对象赋值到变量a,因此a指向的对象发生了改变,id(a) 的值也与原先不同。

再来看一个列表的例子

  1. a = [1,2,3]
  2. print(id(a)) # 输出 140022851303976
  3. a[0]=5
  4. print(a) # 输出 [5, 2, 3]
  5. print(id(a)) # 输出 140022851303976
  6. a.append(5)
  7. print(a) # 输出 [5, 2, 3, 5]
  8. print(id(a)) # 输出 140022851303976
  9. b = a
  10. print(id(b)) # 输出 140022851303976
  11. b[0] = 6
  12. print(b) # 输出 [6, 2, 3, 5]
  13. print(a) # 输出 [6, 2, 3, 5]
  14. c = b[:]
  15. print(id(c)) # 输出 140022851006760
  16. print(id(b)) # 输出 140022851303976
  17. c.append(7)
  18. print(c) # 输出 [6, 2, 3, 5, 7]
  19. print(b) # 输出 [6, 2, 3, 5]

上面对a修改元素、添加元素,变量a还是指向原来的对象。

将a赋值给b后,变量b和a都指向同一个对象,因此修改b的元素值也会影响a。

变量c是对b的切片操作的返回值,切片操作相当于浅拷贝,会生成一个新的对象,因此c指向的对象不再是b所指向的对象,对c的操作不会改变b的值。

理解了上面不可变对象和可变对象的区别后,我们再来看一个有趣的问题

  1. class Group(object):
  2. def __init__(self, group_id, members=[]):
  3. self.group_id = group_id
  4. self.members = members
  5. def add_member(self, member):
  6. self.members.append(member)
  7. group1 = Group(1)
  8. group1.add_member("Wang")
  9. group1.add_member("Sun")
  10. print(id(group1)) # 输出 140280211197776
  11. print(group1.members) # 输出 ['Wang', 'Sun']
  12. group2 = Group(2)
  13. group2.add_member("Zhang")
  14. group2.add_member("Li")
  15. print(id(group2)) # 输出 140280211197840
  16. print(group1.members) # 输出 ['Wang', 'Sun', 'Zhang', 'Li']
  17. print(group2.members) # 输出 ['Wang', 'Sun', 'Zhang', 'Li']

明明group1和group2是不同的对象(id值不同),为什么调用group2的add_member方法会影响group1的members?

其中的奥妙就在于__init__函数的第二个参数是默认参数,默认参数的默认值在函数创建的时候就生成了,每次调用都是用了这个对象的缓存。我们检查id(group1.mebers)和id(group2.members),可以发现他们是相同的

  1. print(id(group1.members)) # 输出 140127132522040
  2. print(id(group2.members)) # 输出 140127132522040

所以,group1.members和group2.members指向了同一个对象,对group2.members的修改也会影响group1.members。

好了,问题来了,怎样改代码才能解决上面默认参数的问题呢?

其实很简单,只要传入None作为默认参数,在创建对象的时候动态生成列表,如下

  1. class Group(object):
  2. def __init__(self, group_id, members=None):
  3. self.group_id = group_id
  4. if members is None:
  5. self.members = []
  6. def add_member(self, member):
  7. self.members.append(member)

这样对于不同的group对象,它们的members也是不同的对象,所以不会再出现更新一个group对象的members也会更新另外一个group对象的members了。

发表评论

表情:
评论列表 (有 0 条评论,312人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Java可变对象

    一、不可变类简介 不可变类:所谓的不可变类是指这个类的实例一旦创建完成后,就不能改变其成员变量值。如JDK内部自带的很多不可变类:Interger、Long和String

    相关 Java可变对象

    不可变对象是指一个对象的状态在对象被创建之后就不再变化。不可变对象对于缓存是非常好的选择,因为你不需要担心它的值会被更改。 创建一个不可变类: 1. 将类声明为final

    相关 python 可变可变变量

    不可变变量 操作某个对象时,重新开辟了内存,使其内存地址改变了,我们称其为可变对象,反之称为不可变变量 我们可以通过下面的例子来感受上面的话 x=1

    相关 可变对象可变对象

    前阵子我们聊了下函数的参数传递以及变量赋值的一些内容:[关于函数参数传递,80%人都错了][80] 简单回顾下要点: 1. Python 中的变量不是装有对象的“容器”,而