2014-03-13 29 views
14

picklecopy.deepcopy之間的關係究竟是什麼?他們分享什麼機制,以及如何?醃製與深層鏡檢之間的關係

很明顯,兩者是密切相關的操作,並且共享一些機制/協議,但是我無法將自己的頭圍繞細節。

一些(混亂)的東西,我發現:

  1. 如果一個類定義__[gs]etstate__,它們會在其實例的deepcopy調用。這一開始讓我感到驚訝,因爲我認爲它們是特定於pickle,但後來我發現Classes can use the same interfaces to control copying that they use to control pickling。然而,有沒有文檔__[gs]etstate__如何deepcopying時使用(如何從__getstate__返回的值時,什麼被傳遞到__setstate__?)
  2. 一個天真的替代實施deepcopypickle.loads(pickle.dumps(obj))。然而,這不可能等同於深拷貝,因爲如果一個類定義了一個__deepcopy__操作,就不會使用這種基於pickle的deepcopy實現來調用它。 (I也偶然發現deepcopy的比鹹菜更一般的,並有許多類型,其是deepcopyable,但不與pickle的聲明。)

(1)表示的通用性,而(2)表示的差pickledeepcopy

最重要的是,我發現這兩種矛盾的說法:

copy_reg:鹹菜,cPickle時,並複製模塊酸洗/複製這些對象

時使用這些功能

copy模塊不使用copy_reg註冊模塊

此,一方面,是pickledeepcopy之間的關係/共性的另一個跡象,而在另一方面,有助於我的困惑......

[我的經驗是python2.7 ,但我也很欣賞任何有關python2和python3之間pickle/deepcopy差異的指針]

回答

2

您不應該被(1)和(2)混淆。一般來說,Python試圖爲缺少的方法包含明智的後備。 (例如,它是足夠的,以便有一個迭代類來定義__getitem__,但它可能是更有效,也能實現__iter__。像__add__操作類似,可選__iadd__等)

__deepcopy__是最專業方法deepcopy()將查找,但如果它不存在,回落到鹹菜協議是一個明智的做法。它並不真正調用dumps()/loads(),因爲它不依賴於中間表示形式的字符串,但它會間接地使用__getstate____setstate__(通過__reduce__),正如您觀察到的那樣。

目前,the documentation還指出

...複製模塊不使用copy_reg註冊模塊。

但似乎是a bug that has been fixed in the meantime(可能,2.7分支在這裏沒有得到足夠的關注)。另外請注意,這是深入集成到Python(至少現在); object類本身實現__reduce__(及其版本化的_ex變體),它指的是copy_reg.__newobj__用於創建給定對象派生類的新實例。

+0

哦,我在Python 2和3之間唯一的區別就是'copy_reg'模塊被重命名爲'copyreg'。 –

+0

這使事情更清晰。謝謝 – shx2

+1

呃,不。你有點不對。 'deepcopy'實際上不使用'__getstate__'和'__setstate__'。 'deepcopy'按順序使用'__deepcopy__',pickle的dispatch_table,'__reduce__'和'__reduce_ex__' ...。檢查代碼。 http://svn.python.org/projects/python/trunk/Lib/copy.py –

7

好吧,我必須閱讀這個源代碼,但它看起來像一個非常簡單的答案。 http://svn.python.org/projects/python/trunk/Lib/copy.py

copy查找一些它知道的構造是什麼樣的(在_copy_dispatch字典中註冊,而當它不知道如何複製的基本類型,它進口copy_reg.dispatch_table ...內建的類型,其是pickle註冊其知道用於生成對象的新副本的方法的地方。本質上,它是一種對象類型的字典和「產生新對象的函數」 - 這個「產生新對象的函數」幾乎就是當你爲對象編寫__reduce____reduce_ex__方法時寫的如果這些之一是缺少或需要幫助,它推遲到__setstate____getstate__等方法。

所以這是copy,基本上......(有一些附加條款...)

def copy(x): 
    """Shallow copy operation on arbitrary Python objects. 

    See the module's __doc__ string for more info. 
    """ 

    cls = type(x) 

    copier = _copy_dispatch.get(cls) 
    if copier: 
     return copier(x) 

    copier = getattr(cls, "__copy__", None) 
    if copier: 
     return copier(x) 

    reductor = dispatch_table.get(cls) 
    if reductor: 
     rv = reductor(x) 
    else: 
     reductor = getattr(x, "__reduce_ex__", None) 
     if reductor: 
      rv = reductor(2) 
     else: 
      reductor = getattr(x, "__reduce__", None) 
      if reductor: 
       rv = reductor() 
      else: 
       raise Error("un(shallow)copyable object of type %s" % cls) 

deepcopy不相同如上所述,但除此之外還要檢查每個對象,並確保每個新對象都有一個副本,而不是poi參考。 deepcopy建立它自己的_deepcopy_dispatch表(字典),其中它註冊的功能,以確保產生的不必原稿

因此寫入__reduce__方法(可能與在copy_reg.dispatch_table註冊的__reduce__功能生成的)指針引用該新對象(或類似)並註冊copy_reg,應該使copydeepcopy也做他們的事情。

+2

所以它看起來像「複製不使用copy_reg」是有點謊言。它在封面下使用它。 :) –