返回介绍

建议38:使用 copy 模块深拷贝对象

发布于 2024-01-30 22:19:09 字数 4382 浏览 0 评论 0 收藏 0

在正式讨论本节内容之前我们先来了解一下浅拷贝和深拷贝的概念:

浅拷贝(shallow copy):构造一个新的复合对象并将从原对象中发现的引用插入该对象中。浅拷贝的实现方式有多种,如工厂函数、切片操作、copy模块中的copy操作等。

深拷贝(deep copy):也构造一个新的复合对象,但是遇到引用会继续递归拷贝其所指向的具体内容,也就是说它会针对引用所指向的对象继续执行拷贝,因此产生的对象不受其他引用对象操作的影响。深拷贝的实现需要依赖 copy 模块的deepcopy()操作。

下面我们通过一段简单的程序来说明浅拷贝和深拷贝之间的区别。

import copy
class Pizza(object):
     def __init__(self,name,size,price):
         self.name=name
         self.size=size
         self.price=price
     def getPizzaInfo(self):      
①获取Pizza
相关信息
         return self.name,self.size,self.price
     def showPizzaInfo(self):     
②显示Pizza
信息
         print "Pizza name:"+self.name
         print "Pizza size:"+str(self.size)
         print "Pizza price:"+str(self.price)
     def changeSize(self,size):
         self.size=size
     def changePrice(self,price):
         self.price=price
class Order(object):          
③订单类
     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 infomation:"
customer1.getorderdetail()
print "-------------------------------"

程序描述的是客户在Pizza店里下了一个订单,并将具体的订单信息打印出来的场景。运行输出结果如下:

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

假设现在客户2也想下一个跟客户1一样的订单,只是要将预定的水果披萨的尺寸和价格进行相应的修改。于是服务员拷贝了客户1的订单信息并做了一定的修改,代码如下:

customer2=copy.copy(customer1)
print "order 2 customer name:"+customer2.customername
customer2.changeName("li")
customer2.getPizza(2).changeSize(9)
customer2.getPizza(2).changePrice(30)
print "customer2 order infomation:"
customer2.getorderdetail()
print "-------------------------------------"

上面这段程序的输出也没有什么问题,完全满足了客户2的需求。输出结果如下所示:

order 2 customer name:zhang
customer2 order infomation:
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的订单信息:

print "customer1 order information:"
customer1.getorderdetail()

你会发现客户1的订单内容除了客户姓名外,其他的居然和客户2的订单具体内容一样了。

------------------------------------
customer1 order infomation:
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根本没要求修改订单的内容,这样的结果必定会直接影响到客户满意度。问题出现在哪里?这是我们本节要重点讨论的内容。我们先来分析客户1和客户2订单内容的关系图,如图4-1所示。

图4-1 客户1和客户2订单的关系示意图

customer1中的pizzaList是一个由Pizza对象组成的列表,其中存放的实际是对一个个具体Pizza对象的引用,在内存中就是一个具体的地址,可以通过查看id得到相关信息。

print id(customer1.pizzaList[0])   
输出14099440
print id(customer1.pizzaList[1])   
输出14101392
print id(customer1.pizzaList[2])   
输出 14115344
print id(customer1.pizzaList)    
输出 13914800

customer2的订单通过copy.copy(customer1) 获得,通过id函数查看customer2中pizzaList的具体Pizza对象你会发现它们和customer1中的输出是一样的(读者可以自行验证)。这是由于通过copy.copy()得到的customer2是customer1的一个浅拷贝,它仅仅拷贝了pizzalist里面对象的地址而不对对应地址所指向的具体内容(即具体的pizza)进行拷贝,因此customer2中的pizzaList所指向的具体内容是和customer1中一样的,如图4-1所示。所以对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(customer1)改为copy.deepcopy()来实现(请读者自行验证)。关于对象拷贝读者可以查看网页http://en.wikipedia.org/wiki/Object_copy进行阅读扩展。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文