Python中的可變對象和不可變對象

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.membersgroup2.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

微信公衆號:小鑫的代碼平常

相關文章
相關標籤/搜索