>>> from env_helper import info; info()
页面更新时间: 2024-03-14 20:44:38
运行环境:
Linux发行版本: Debian GNU/Linux 12 (bookworm)
操作系统内核: Linux-6.1.0-18-amd64-x86_64-with-glibc2.36
Python版本: 3.11.2
4.3. 使用 copy
模块深拷贝对象¶
在正式讨论本节内容之前我们先来了解一下浅拷贝和深拷贝的概念:
浅拷贝(shallow copy ):构造一个新的复合对象并将从原对象中发现的引用插人该对象中。浅拷贝的实现方式有多种,如工厂函数数、切片操作、copy模块中的
copy()
操作等。深拷贝(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
你会发现客户1的订单内容除了客户姓名外,其他的居然和客户2的订单具体内容一样了。
这是怎么回事呢?客户1本没要求修改订单的内容,样的结果必定会直接影响到客 户满意度。 问题出现在哪里?这是我们本节要重点讨论的内容。我们先来分析客户1和客户 2订单内容的关系图,如图所示。
图4-1 客户1和客户2订单的关系示意图
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))
140156661010128
140156661011152
140156661010832
140156660989568
customer2的订单通过 copy.copy(customerl)
获得,通过 id
函数査看
customer2
中
pizzaList的具体Pizza对象你会发现它们和customerl中的输出是一样的(读者可以自行验
证)。这是由于通过 copy.copy()
得到的 customer2
是customer1
的一个浅拷贝,它仅仅拷贝了
pizzalist里面对象的地址而不对对应地址所指向的具体内容(即具体的pizza)进行拷贝,因此
customer2
中的 pizzaList
所指向的具体内容是和 customer1
中一样的,如图所示。
所以对pizza fruit的修改直接影响了 customer1
的订单内容。实际上在包含引用的数据结构中, 浅拷贝并不能进行彻底的拷贝,
当存在列表、字典等不可变对象的时候,它仅仅拷贝其引用地址。
要解决上述问题需要用到深拷贝,深拷贝不仅拷贝引用也拷贝引用所指向的对象,因此深拷贝得到的对象和原对象是相互独立的。
上面的例子充分展示了浅拷贝和深拷贝之间的差异,在实际应用中要特別注意这两者之
间的区别。 实际上Python copy
模块提供了与浅拷贝和深拷贝对应的两种方法的实现, 通过
名字便可以轻易进行区分,模块在拷贝出现异常的时候会拋出 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()
来实现。