淺拷貝 VS 深拷貝
# In[] # list 生成一個新的引用物件,只是用alst完成初始化 alst = [1,2,3,4,5] blst=list(alst) alst.append(6) print(blst) # In[] alst = [1,2,3,4,5] blst=alst # 淺拷貝,兩者同時變化 alst.append(6) print(blst) # In[] from copy import copy alst = [1,2,3,4,5] blst=alst.copy() # 淺拷貝,兩者同時變化 alst.append(6) print(blst) # In[] import copy alst = [1,2,3,4,5] blst=copy.deepcopy(alst) # 深拷貝,可以認為是用alst初始化一個新的物件 alst.append(6) print(blst)
此處,對可變型別而言,深拷貝其實就是建立了一個新的引用物件,可以認為原來的只是用來做初始化用的。新舊有變化對彼此都沒有影響了。淺拷貝可以認為就是起了個別名。
由於這層原因,當可變型別作為函式引數時,尤其要謹慎處理。首先考慮初始化,如果存在None,該怎麼處理,其次如果你不想修改傳入引數的值,那麼要給類或者函式的屬性建立一個副本或者說只是拿傳入的引數作為初始化,務必保證,函式內部修改可變型別變數時,形參傳入的變數不會受到影響。
''' the default value of paramter is None ''' import copy # tag::BUS_CLASS[] class Bus: def __init__(self, passengers=None): if passengers is None: self.passengers = [] else: self.passengers = list(passengers) def pick(self, name): self.passengers.append(name) def drop(self, name): self.passengers.remove(name) # end::BUS_CLASS[] bus1 = Bus(['a','b','c']) bus2 = copy.copy(bus1) bus1.pick('d') bus1.drop('a') print(bus2.passengers) bus3= copy.deepcopy(bus1) print(bus3.passengers) bus1.pick('a') bus1.drop('d') print(bus3.passengers) print(bus1.passengers)
此時如果對預設引數處理不好,會引起很詭異的事情。但是如果用list生成一個新的物件,就會避免這個問題。
''' Mutable Types as Parameter Defaults:Bad Idea ''' # tag::HAUNTED_BUS_CLASS[] class HauntedBus: """A bus model haunted by ghost passengers""" def __init__(self, passengers=[]): # <1> # should be list function to initialization the attribute self.passengers = passengers #list(passengers) # <2> def pick(self, name): self.passengers.append(name) # <3> def drop(self, name): self.passengers.remove(name) # end::HAUNTED_BUS_CLASS[] bus1 = HauntedBus(['Alice', 'Bill']) print(bus1.passengers) bus1.pick('Charlie') bus1.drop('Alice') print(bus1.passengers) bus2 = HauntedBus() bus2.pick('Carrie') print('bus2.passengers:',bus2.passengers) bus3 = HauntedBus() print('bus3.passengers:',bus3.passengers) bus3.pick('Dave') print('bus3.passengers:',bus3.passengers) print(bus2.passengers is bus3.passengers) print('bus1.passengers:',bus1.passengers)
還有,這個程式,把形參改變了,本來不該改變的。
''' here the attribute passengers should be initialized using a list func. ''' # tag::TWILIGHT_BUS_CLASS[] class TwilightBus: """A bus model that makes passengers vanish""" def __init__(self, passengers=None): if passengers is None: self.passengers = [] # <1> else: # this can change the passengers when you change self.passengers self.passengers = passengers #<2> def pick(self, name): self.passengers.append(name) def drop(self, name): self.passengers.remove(name) # <3> # end::TWILIGHT_BUS_CLASS[] basketball_team = ['Sue', 'Tina', 'Maya', 'Diana', 'Pat'] bus = TwilightBus(basketball_team) bus.drop('Tina') bus.drop('Pat') print(basketball_team)
這幾個程式大家都看到了,其實很關鍵的一個地方是list函式的使用。test = list(alst)相當於將alst 進行深拷貝,改變alst時,test不會隨之改變,改變test時,alst也不會有影響。也可以理解為將alst作為引數,對元素追個轉換,生成一個新的list並返回給test。因此,兩者之間不再有影響。