浅拷贝 VS 深拷贝
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | # 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 copyalst = [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,该怎么处理,其次如果你不想修改传入参数的值,那么要给类或者函数的属性创建一个副本或者说只是拿传入的参数作为初始化,务必保证,函数内部修改可变类型变量时,形参传入的变量不会受到影响。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | '''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生成一个新的对象,就会避免这个问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | '''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) |
还有,这个程序,把形参改变了,本来不该改变的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | '''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。因此,两者之间不再有影响。