1

我认为解释这种情况的最好方法是举个例子:

>>> 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)似乎正在重新实例化bob并且是alice.brother单独的。如何使用shelve/来“修复”这个问题pickle?是否有可能db['alice'].brother指向db['bob']或类似的东西?请注意,我不想只比较两者,我需要两者实际上是相同的。

正如Blckknght所建议的,我尝试一次腌制整个字典,但问题仍然存在,因为它似乎分别腌制每个键。

4

3 回答 3

2

我相信您看到的问题来自shelve模块存储其值的方式。每个值都独立于架子中的其他值进行腌制,这意味着如果将同一对象作为值插入到多个键下,则不会在键之间保留标识。但是,如果单个值具有对同一对象的多个引用,则标识将在该单个值中维护。

这是一个例子:

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插入到数据库中的对象的两个版本的身份,一个直接在 key 下'a',另一个在'another_a'. 它不起作用,因为单独的值是单独腌制的,因此它们之间的身份丢失了。

第二个打印测试是否保留a了存储在密钥下的两个引用'two_a_references'。因为列表是一次性腌制的,所以保留了身份。

因此,要解决您的问题,您有几个选择。一种方法是避免测试身份,并依靠__eq__各种对象类型中的方法来确定两个对象在语义上是否相等,即使它们不是同一个对象。另一种方法是将所有数据捆绑到一个对象(例如字典)中,然后您将使用该对象保存pickle.dump和恢复pickle.load而不是使用shelve(或者您可以将此配方调整为shelve从文档链接的持久字典,并且几乎可以做到这一点)。

于 2013-07-14T05:16:21.270 回答
1

在 Python 中,适当的方法是在 Person 类中实现__eq__和函数,如下所示:__ne__

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

于 2013-07-14T04:44:43.260 回答
1

问题

要与您保持身份,shelve您需要通过pickle阅读此来保持身份。

解决方案

此类将所有对象保存在其类站点上,如果标识相同,则将其恢复。您应该能够从中继承。

>>> 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'
于 2013-07-14T18:30:52.117 回答