2013-07-14 22 views
1

我想說明情況的最好辦法是用一個例子:Python的情況下將其關閉後更改

>>> class Person: 
...  def __init__(self, brother=None): 
...   self.brother = brother 
... 
>>> bob = Person() 
>>> alice = Person(brother=bob) 
>>> import shelve 
>>> db = shelve.open('main.db', writeback=True) 
>>> db['bob'] = bob 
>>> db['alice'] = alice 
>>> db['bob'] is db['alice'].brother 
True 
>>> db['bob'] == db['alice'].brother 
True 
>>> db.close() 
>>> db = shelve.open('main.db',writeback=True) 
>>> db['bob'] is db['alice'].brother 
False 
>>> db['bob'] == db['alice'].brother 
False 

預期的輸出對於兩個比較是True一次。但是,pickle(由shelve使用)似乎分別重新實例化bobalice.brother。我怎樣才能「修復」這個使用shelve/pickledb['alice'].brother可能指向db['bob']或類似的東西嗎?注意我不想只比較兩者,我需要兩者實際上相同。

正如Blckknght建議的那樣,我試着一次酸洗整個詞典,但問題依然存在,因爲它似乎分別醃製每個鍵。

+0

什麼是'Person'? – user2357112

+0

@ user2357112只是一個示例類,不應該有所作爲...請參閱http://pastebin.com/hCxSU54n – Alex

回答

2

我相信你看到的問題來自於shelve模塊存儲其值的方式。每個值都獨立於貨架上的其他值進行pickle,這意味着如果同一個對象作爲多值鍵下的值插入,則鍵之間的身份不會被保留。但是,如果一個值具有對同一對象的多個引用,則該身份將保持在該單個值內。

下面是一個例子:

a = object() # an arbitrary object 
db = shelve.open("text.db") 
db['a'] = a 
db['another_a'] = a 
db['two_a_references'] = [a, a] 
db.close() 

db = shelve.open("text.db") # reopen the db 
print(db['a'] is db['another_a']) # prints False 
print(db['two_a_references'][0] is db['two_a_references'][1]) # prints True 

第一印刷試圖確認對象a的兩個版本分別插入在數據庫中,一個直屬重點'a',另一個'another_a'下的身份。它不起作用,因爲單獨的值分開醃製,因此它們之間的身份已經丟失。

第二次打印測試是否保留了存儲在密鑰'two_a_references'下的兩個對a的引用。因爲這份清單是一口氣醃製的,所以身份保持不變。

因此,爲了解決您的問題,您有幾個選擇。一種方法是避免測試身份,並依靠各種對象類型中的__eq__方法來確定兩個對象是否在語義上相同,即使它們不是相同的對象。另一種方法是將所有數據捆綁到單個對象中(例如字典),然後用pickle.dump保存並用pickle.load恢復,而不是使用shelve(或者您可以修改this recipe for a persistent dictionary,它與shelve文檔關聯,非常多)。

+0

正是!事情是,我已經將所有數據都捆綁在一本大字典中,但正如您所提到的,由於每個密鑰都分開醃漬,因此不會保留身份。我會嘗試去討論'shelve'選項,只使用'pickle',但我懷疑它會有幫助,因爲'shelve' [只是一個讓酸洗變得更簡單的類](http://hg.python.org/ CPython的/文件/ 2.7 /庫/ shelve.py)。 – Alex

+0

只有使用[持續字典配方](http://code.activestate.com/recipes/576642/)並將所有內容捆綁在一個大字典中才能解決問題。謝謝您的回答! – Alex

1

適當的方式,在Python,是落實__eq____ne__功能Person類裏面,像這樣的:一般

class Person(object): 

    def __eq__(self, other): 
     return (isinstance(other, self.__class__) 
      and self.__dict__ == other.__dict__) 

    def __ne__(self, other): 
     return not self.__eq__(other) 

,這應該是足夠了,但如果這是真正的數據庫對象和有一個主鍵,檢查該屬性而不是self.__dict__會更有效。

1

問題

不同,需要保留身份與pickleread thisshelve保留身份。

解決方案

本級撲救於它的類網站中的所有對象,並恢復他們,如果身份是一樣的。你應該能夠從中繼承。

>>> class PickleWithIdentity(object): 
    identity = None 
    identities = dict() # maybe use weakreference dict here 
    def __reduce__(self): 
     if self.identity is None: 
      self.identity = os.urandom(10) # do not use id() because it is only 4 bytes and not random 
      self.identities[self.identity] = self 
     return open_with_identity, (self.__class__, self.__dict__), self.__dict__ 


>>> def open_with_identity(cls, dict): 
    if dict['identity'] in cls.identities: 
     return cls.identities[dict['identity']] 
    return cls() 

>>> p = PickleWithIdentity() 
>>> p.asd = 'asd' 
>>> import pickle 
>>> import os 
>>> pickle.loads(pickle.dumps(p)) 
<__main__.PickleWithIdentity object at 0x02D2E870> 
>>> pickle.loads(pickle.dumps(p)) is p 
True 

進一步的問題可能會發生,因爲國家可能會被改寫:

>>> p.asd 
'asd' 
>>> ps = pickle.dumps(p) 
>>> p.asd = 123 
>>> pickle.loads(ps) 
<__main__.PickleWithIdentity object at 0x02D2E870> 
>>> p.asd 
'asd' 
相關問題