Python:使用copy模塊深拷貝對象

    Python語言中有深拷貝和淺拷貝的概念,那什麼是深拷貝,淺拷貝呢?

   淺拷貝(shallow copy):構造一個新的複合對象並將從原對象中發現的引用(也就是地址,而不是地址所指向的內容)插入該對象中。淺拷貝的實現方法有很多種,如工廠函數、切片操作、copy模塊中的copy操作。

   深拷貝(deep copy):也構造一個新的複合對象,但是遇到引用會繼續遞歸拷貝其所指向的內容,也就是說他會針對引用所指向的對象繼續執行拷貝,因此產生的對象不受其它對象操作的影響。深拷貝的實現需要依賴copy模塊的deepcopy()操作

先看下面例子吧,定義了一個Pizza類和Order類,分別反應Pizza信息和訂單信息

#coding=utf-8
import copy
class Pizza(object):
    """
    Pizza類
    """
    def __init__(self,name,size,price):
        self.name = name
        self.size = size
        self.price = price

    def showPizzaInfo(self):
        print "".join(('Pizza name:',self.name))  
        print "".join(("Pizza size:",str(self.size)))
        print "".join(('Pizza price:',str(self.price)))
        #字符串的拼接儘量使用join()函數,不要使用+號,因爲前者效率更高,而且更Pythonnic

    def getPizzaInfo(self):
        return self.name,self.size,self.price

    def changename(self,name):
        self.name = name

    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 "".join(('customer name:',self.customername))
        for one in self.pizzalist:
            one.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 "--------------------"

上面代碼描述的意思是客戶1下了一個訂單,並且把具體訂單打印出來的場景。運行結果如下:

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

Pizza price:35

--------------------

假設客戶2也想跟客戶1下一樣的訂單,只是改變了水果Pizza的尺寸,那麼價格也應該作相應的修改。於是想到拷貝客戶1的訂單信息並做一定的修改,代碼如下:

customer2 = copy.copy(customer1)
print "".join(["order2 customer name:",customer2.customername])
customer2.changename("Li")
customer2.getPizza(2).changesize(9)
customer2.getPizza(2).changeprice(30)
print "customer2 order information:"
customer2.getorderdetail()
print "----------------------"

輸出結果如下:

order2 customer name:zhang

customer2 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的要求的,修改客戶2訂單信息後再來看看客戶1訂單信息,

print "customer1 order infomation:"
customer1.getorderdetail()
print "----------------------"

結果如下:

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訂單信息除了客戶姓名外,其它和客戶2訂單信息居然一樣了。如果這真是一個生成用的系統,必將會影響客戶的滿意度,因爲客戶1沒有要求修改訂單信息呀。這怎麼回事呢?

   customer1 中的pizzalist是由Pizza對象組成的列表,其中存放的實際是對一個個具體Pizza對象的引用,在內存中就是一個具體的位置,可以打印出它們的id

print id(customer1.pizzalist[0]) 
print id(customer1.pizzalist[1])
print id(customer1.pizzalist[2])
print id(customer1.pizzalist)

結果如下:

140206506600080

140206506602064

140206506652560

140206507110552

 customer2的訂單是由copy.copy(customer1)獲得,把customer2的pizzalist的id打印出來與customer1的pizzalist同時打印出來,代碼如下:

print id(customer1.pizzalist[0])
print id(customer1.pizzalist[1])
print id(customer1.pizzalist[2])
print id(customer1.pizzalist)
print "--------------------------"
print id(customer2.pizzalist[0])
print id(customer2.pizzalist[1])
print id(customer2.pizzalist[2])
print id(customer2.pizzalist)

打印結果如下

140206506600080

140206506602064

140206506652560

140206507110552

--------------------------

140206506600080

140206506602064

140206506652560

140206507110552

發現是一樣的,這是由於copy.copy()得到的是一個淺拷貝,它僅僅拷貝了對象的地址而不對對應地址所指的具體內容進行拷貝。

   包含引用的數據結構中,淺拷貝並不能進行徹底拷貝,當存在字典、列表等可變對象時,它僅僅拷貝了其引用地址,並沒有拷貝對應地址的內容。要解決這個問題就需要使用深拷貝,深拷貝不僅拷貝了引用也拷貝了引用所指向的對象(也就是內容),因此深拷貝得到的對象與原對象是相互獨立的。

如果將上面程序改爲customer2 = copy.deepcopy(customer1)來實現就不會出現這樣的問題。

下面簡單介紹一些copy模塊的一些知識:

copy模塊的主要對外接口就是copy()和deepcopy()。

在定義類的時候,通過定義__copy__和__deepcopy__方法,可以改變copy的默認行爲。下面是一個簡單的例子:

#coding=utf-8
import copy
class CopyObj(object):
    def __repr__(self):
        return "CopyObj"

    def __copy__(self):
        return "copy"
obj = CopyObj()
obj1 = copy.copy(obj)
obj2 = copy.deepcopy(obj)
print obj
print obj1
print obj2
print "-----------------"

class Copyobj(object):
    def __repr__(self):
        return "Copyobj"
    def __deepcopy__(self,meno=None):
        return "deep copy"
a = Copyobj()
b = copy.copy(a)
c = copy.deepcopy(a)

print a
print b
print c

輸出結果如下:

CopyObj

copy

CopyObj

-----------------

Copyobj

Copyobj

deep copy


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章