2013-05-07 34 views
4

爲什麼下面的代碼在作爲腳本運行時會產生錯誤?在交互式shell(剪切和粘貼)中運行時不會產生錯誤。不一致的cPickle

import cPickle as pickle 

val1 = dict(fooblah=[], xy=[]) 
pickval1 = pickle.dumps(val1, protocol=2) 

val2 = pickle.loads(pickval1) 
assert val1 == val2 

pickval2 = pickle.dumps(val2, protocol=2) 
assert pickval1 == pickval2, (pickval1, pickval2) 

在泡菜的區別是下面:

$ python /tmp/picklefun.py 
Traceback (most recent call last): 
    File "/tmp/picklefun.py", line 10, in <module> 
    assert pickval1 == pickval2, (pickval1, pickval2) 
AssertionError: ('\x80\x02}q\x01(U\x07fooblahq\x02]U\x02xyq\x03]u.', 
       '\x80\x02}q\x01(U\x07fooblah]U\x02xy]u.') 

回答

2

如果用

exec "val1 = dict(fooblah=[], xy=[])" 

更換線

val1 = dict(fooblah=[], xy=[]) 

然後斷言再次通過。

爲什麼?答案就在於cPickle的奧祕之中。它有一個優化,看看有些對象的引用計數器是否小於2,並避免在這種情況下的幾個字節(通常用於檢測週期或同一個可能的大字符串的多個外觀)。這是關於字符串對象「fooblah」和「xy」。在exec的情況下或交互式運行的情況下,在您醃菜的時候,字符串中僅剩下字符串的唯一引用;引用計數器是1,所以cPickle避免了幾個字節。但是,如果您將該示例作爲模塊編寫,那麼該模塊在那時仍然存在,並且將用作常量的字符串另存爲另一個引用。

編輯澄清:我們酸洗第二次,我們將醃製一向鮮鍵從在unpickle未來字典 - 參考計數器1於是斷言通過當且僅當按鍵哪裏還的參考計數器1第一次。

+0

這可以是雙用替換EVAL檢查: ''' VAL1 =字典() EXEC 'VAL1 [ 「fooblah」] = []' EXEC 'VAL1 [ 「XY」] = []' ' '' – user650654 2013-05-09 15:22:15

1

似乎被cPickle造成的,因爲它不會發生使用純老pickle(我能重現你的錯誤)。

這就是爲什麼,1級......我會繼續研究,因爲這是一個有趣的發現!

更新:

的cPickle的文檔(ESP腳註)保證對象將總是/讀取/正確,但不保證的情況下(或針對保留的),所述串行化數據是總是相等的。可能不是意想不到的行爲,但值得注意。

http://docs.python.org/2/library/pickle.html#module-cPickle

+0

我也轉載了:Windows RT上的Python 2.7.3。 'cPickle'失敗; '鹹菜'沒有。 – michaelb958 2013-05-07 23:23:36

+0

很高興知道您能夠重現它。有兩個問題需要回答(1)爲什麼是pickval1!= pickval2,以及(2)爲什麼它在作爲腳本運行時產生錯誤,而不是在交互式運行時產生錯誤。第二個更令人擔憂的是恕我直言。 – user650654 2013-05-08 01:23:05

+1

即使序列化版本不同,pickle.loads的結果也是相同的,這讓我擔心的是(2)。這是非常有趣的,但希望有更多的人瞭解你所發現的差異可以啓發我們。但是我知道這兩個字符串代表了同一個對象,我希望能夠輕鬆一點。 – mdscruggs 2013-05-08 01:28:47