浅拷W和深拷贝的概念:
浅拷贝(shallow copy ):构造一个新的复合对象并将从原对象中发现的引用插人该对象 中。浅拷贝的实现方式有多种,如工厂函数数、切片操作、copy模块中WCoPy操作等。
深拷贝(deepCoPy):也构造一个新的复合对象,但是遇到引用会继续递归拷贝其所指 向的具体内容,也就是说它会针对引用所指向的对象继续执行拷贝, 因此产生的对象 不受其他引用对象操作的影响^深拷贝的实现需要依赖copy模块的 deepcopy() 操作。
下面我们通过一段简单的程序来说明浅拷贝和深拷贝之间的区别。
import copy class Pizza(): def init (self,name,size,price): self.name=name self.size=size self.price=price def getPizzaInfo(self): return self.name,self.size,self.price def showPizzaInfo(self): print(“Pizza name :”+self.name) print(“Pizza size:”+self.size) print(“Pizza price:”+self.price) def changeSize(self,size): self.size=size def changePrice(self,price): self.price=price class Order(): def init(self,name): self.customername=name self.pizzaList=[] self.pizzaList.append(Pizza(“mushroom”,“12”,“30”)) def ordermore(self,pizza): self.pizzaList.append(pizza) def changeName(self,name): self.customername=name def getorderdetail(self): print(“customer name:”+self.customername) for i in self.pizzaList: i.showPizzaInfo() def getPizza(self,number): return self.pizzaList[number] customer1=Order(“zhang”) customer1.ordermore(Pizza(“seafood”,“9”,“40”)) customer1.ordermore(Pizza(“fruit”,“12”,“35”)) print(“customer1 order information:”) customer1.getorderdetail() customer1 order information: customer name:zhang Pizza name :mushroom Pizza size:12 Pizza price:30 Pizza name :seafood Pizza size:9 Pizza price:40 Pizza name :fruit Pizza size:12 Pizza price:35 程序描述的是客户在Pizza店里下了一个订单,并将具体的订单信息打印出来的场景。 运行输出结果如下:
customer2=copy.copy(customer1) print(“order2 customer name”) customer2.customername customer2.changeName(“li”) customer2.getPizza(2).changeSize(“9”) customer2.getPizza(2).changePrice(“30”) print(“customer 2 order information:”) customer2.getorderdetail() order2 customer name customer 2 order information: customer name:li Pizza name :mushroom Pizza size:12 Pizza price:30 Pizza name :seafood Pizza size:9 Pizza price:40 Pizza name :fruit Pizza size:9 Pizza price:30 假设现在客户2也想下一个跟客户1 —样的订单,只是要将预定的水果披萨的尺寸和价 格进行相应的修改。于是服务员拷贝了客户]的订单信息并做了一定的修改,代码如下;
在修改完客户2的订单信息之后,现在我们再来检査一下客户1的订单信息:
print(“customer1 order information:”) customer1.getorderdetail() customer1 order information: customer name:zhang Pizza name :mushroom Pizza size:12 Pizza price:30 Pizza name :seafood Pizza size:9 Pizza price:40 Pizza name :fruit Pizza size:9 Pizza price:30 你会发现客户丨的订单内容除了客户姓名外,其他的居然和客户2的订单具体内容一样了。
这是怎么回事呢?客户丨根本没要求修改订单的内容f这样的结果必定会直接影响到客 户满意度。问题出现在哪里?这是我们本节要重点讨论的内容。我们先来分析客户1和客户 2订单内容的关系图,如图所示。 image1
customerl中的pizzaList是一个由Pizza对象组成的列表,其中存放的实际是对一个个 具体Pizza对象的引用,在内存中就是一个具体的地址,可以通过査看id得到相关信息a
print(id (customer1 .pizzaList [0])) print(id (customer1 .pizzaList [1])) print(id (customer1 .pizzaList [2])) print(id (customer1.pizzaList)) 139711528292304 139711528291856 139711528292248 139711528345224 customer2的订单通过copy.copy(customerl)获得,通过id函数査看customer2中 pizzaList的具体Pizza对象你会发现它们和customerl中的输出是一样的(读者可以自行验 证)。这是由于通过copy.copy()得到的customer2是customerl的一个浅拷贝,它仅仅拷贝了 pizzalist里面对象的地址而不对对应地址所指向的具体内容(即具体的pizza)进行拷贝,因 此cust〇mer2中的pizzaList所指向的具体内容是和customer!:中一样的,如图4-1所示。所 以对pizza fruit的修改直接影响了 customerl的订单内容。实际上在包含引用的数据结构中, 浅拷瓜并不能进行彻底的拷贝,当存在列表、字典等不可变对象的时候,它仅仅拷贝其引用 地址。要解决上述问题需要用到深拷贝,深拷贝不仅拷贝引用也拷贝引用所指向的对象,因 此深拷贝得到的对象和原对象是相互独立的。 上面的例子充分展示了浅拷贝和深拷贝之间的差异,在实际应用中要特別注意这两者之 间的区别。实际上Python coj>y模块提供了与浅拷贝和深拷贝对应的两种方法的实现,通过 名字便可以轻易进行区分,模块在拷贝出现异常的时候会拋出copy.error。
copy.copy(x) ; Return a shallow copy of x. copy, deepcopy <x) ; Return a deep copy of x. exception copy- error : Raised for module specific errors. 实际上,上面的程序应该将customer2=copy.copy(customer])改为copy.deepcopy()来实 现(请读者自行验证)。