对我来说看起来像是一个错误,但也许有人会对此有一个合理的解释。 考虑以下代码:
class Test(ndb.Model):
a= ndb.IntegerProperty()
p = ndb.PickleProperty(default={})
现在执行以下操作:
>> t1 = Test()
>> t1.p['a'] = 1
>> t1.p['b'] = 2
{'a': 1, 'b': 2}
到目前为止,一切都很好。但现在:
>> t2 = Test()
>> t2.p
{'a': 1, 'b': 2}
全新的 t2 实例已被分配 t1 的 p 值?!!
除了错误之外还有什么解释吗? 请注意,对 t1 执行 put() 操作不会改变行为。
最佳答案
在 Python 中,默认参数被评估一次——因此您使用的是单个 dict
(您的 default={ }
是每个进程的一个字典,而不是每个实体一个!)跨越所有 Test
类型的实体,这些实体恰好位于同一进程内,并且 p
未明确设置。
如果你这样做t=Test(p={})
,那么t
就可以了,有自己的dict
。如果您执行t=Test()
,然后t.p = {}
,您也会没事的。但是,如果您不以某种方式设置实体的特定 p
,它将使用与该实体的所有实体使用的相同的默认 dict
类型,在同一进程内,没有显式设置 p
。
当您放置
一个Test
实体时,进入数据存储的是其p
当时的 pickle “快照”——当您取回
它时,它将恢复到该状态,并且现在与default
single-dict
-per的其他可能使用断开连接-过程。但这些只是这种可疑用法中的更多异常现象。
简单地说,可变默认值在 Python 中并不是一个好主意——人们几乎从来没有正确使用过它们。这适用于对ndb.PickleProperty的调用,至少与对其他Python可调用对象的任何其他调用一样!
添加:如果您需要一个 PickleProperty
来专门保存一个字典,并且发现每次实例化此类实体时显式添加 p={}
太麻烦,子类化 PickleProperty
可能会有所帮助。即:
class DictPickleProperty(ndb.PickleProperty):
def __init__(self, **kwds):
kwds['default'] = kwds.get('default', {})
super(DictPickleProperty, self).__init__(**kwds)
如果 default
未指定 (A) 或 (B) 指定为不是 dict
的内容,您想要执行的操作当然取决于您。这个简单的例子在情况(B)中没有做任何特别的事情——(所以例如 default=[]
仍然会导致问题)——但确实使用了一个新的空 dict
以防万一(一)。
或者,您可以尝试将任何提供的 default
值转换为新的 dict
(因此 []
或 {}
会创建一个新的空 dict
,但许多其他值会引发异常):
kwds['default'] = dict(kwds.get('default', ()))
或者,当然,还有许多其他变体。
关于python - 字典 : new entity taking old property? 的 GAE NDB PickleProperty,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28387723/