惯用列表包装

时间:2011-02-23 21:00:43

标签: google-app-engine python

在Google App Engine中,我将引用的属性列表大致如下:

class Referenced(BaseModel):
    name = db.StringProperty()

class Thing(BaseModel):
    foo_keys = db.ListProperty(db.Key)

    def __getattr__(self, attrname):
        if attrname == 'foos':
            return Referenced.get(self.foo_keys)
        else:
            return BaseModel.__getattr__(self, attrname)

这样,有人可以拥有Thing并说出thing.foos并从中获取合法的内容。当有人说thing.foos.append(x)时会出现问题。这不会保存添加的属性,因为基础的键列表保持不变。所以我很快写了这个解决方案,以便将密钥附加到列表中:

class KeyBackedList(list):
    def __init__(self, key_class, key_list):
        list.__init__(self, key_class.get(key_list))
        self.key_class = key_class
        self.key_list = key_list

    def append(self, value):
        self.key_list.append(value.key())
        list.append(self, value)

class Thing(BaseModel):
    foo_keys = db.ListProperty(db.Key)

    def __getattr__(self, attrname):
        if attrname == 'foos':
            return KeyBackedList(Thing, self.foo_keys)
        else:
            return BaseModel.__getattr__(self, attrname)

这非常适用于概念验证,因为它在调用append时完全按预期工作。但是,我绝不会将此内容提供给其他人,因为他们可能会以其他方式(thing[1:9] = whatevsthing.sort())改变列表。当然,我可以定义所有的__setslice__和诸如此类的东西,但这似乎让我对于讨厌的错误开放。但是,这是我能想到的最好的解决方案。

有没有更好的方法来做我想做的事情(或许在Python库中)?或者我是以错误的方式解决这个问题并试图让事情过于顺利?

1 个答案:

答案 0 :(得分:2)

如果你想修改这样的东西,你不应该改变模型上的__getattr__;相反,你应该写一个custom Property class

正如您所观察到的那样,创建一个可行的“ReferenceListProperty”很困难且涉及很多,并且存在许多微妙的边缘情况。我建议坚持使用密钥列表,并在需要时在代码中获取引用的实体。