我在GAE上的Python编程中重现的模式是从数据存储中获取一些实体,然后可能根据各种条件更改该实体。最后,我需要将.put()实体返回到数据存储,以确保可以保存对其进行的任何更改。
然而,实际上通常没有做出任何改变,最终的.put()只是浪费金钱。如果真的发生变化,如何轻松确保我只放置一个实体?
代码可能类似于
def handle_get_request():
entity = Entity.get_by_key_name("foobar")
if phase_of_moon() == "full":
entity.werewolf = True
if random.choice([True, False]):
entity.lucky = True
if some_complicated_condition:
entity.answer = 42
entity.put()
如果任何条件改变了实体,我可以保持一个“已更改”的标志,但这似乎非常脆弱。如果我忘记将其设置在某处,那么更改将会丢失。
我最终使用
def handle_get_request():
entity = Entity.get_by_key_name("foobar")
original_xml = entity.to_xml()
if phase_of_moon() == "full":
entity.werewolf = True
if random.choice([True, False]):
entity.lucky = True
if some_complicated_condition:
entity.answer = 42
if entity.to_xml() != original_xml: entity.put()
我不会称之为“优雅”。如果对象最终会自动保存自己,那将是优雅的,但我觉得这对于现在来说简单易读。
答案 0 :(得分:4)
为什么不检查结果是否等于(==
)原始结果,因此决定是否保存它。这取决于正确实施的__eq__
,但默认情况下,基于__dict__
的逐字段比较应该这样做。
def __eq__(self, other) :
return self.__dict__ == other.__dict__
(如果你这样做,请确保其他丰富的比较和哈希运算符正常工作。See here。)
答案 1 :(得分:4)
一种可能的解决方案是使用跟踪任何属性更改的包装器:
class Wrapper(object):
def __init__(self, x):
self._x = x
self._changed = False
def __setattr__(self, name, value):
if name[:1] == "_":
object.__setattr__(self, name, value)
else:
if getattr(self._x, name) != value:
setattr(self._x, name, value)
self._changed = True
def __getattribute__(self, name):
if name[:1] == "_":
return object.__getattribute__(self, name)
return getattr(self._x, name)
class Contact:
def __init__(self, name, address):
self.name = name
self.address = address
c = Contact("Me", "Here")
w = Wrapper(c)
print w.name # --> Me
w.name = w.name
print w.name, w._changed # --> Me False
w.name = "6502"
print w.name, w._changed # --> 6502 True
答案 2 :(得分:2)
此回答是我发布的关于Python checksum of a dict的问题的一部分 通过这个问题的答案,我开发了一种生成校验和的方法 db.Model。
这是一个例子:
>>> class Actor(db.Model):
... name = db.StringProperty()
... age = db.IntegerProperty()
...
>>> u = Actor(name="John Doe", age=26)
>>> util.checksum_from_model(u, Actor)
'-42156217'
>>> u.age = 47
>>> checksum_from_model(u, Actor)
'-63393076'
我定义了这些方法:
def checksum_from_model(ref, model, exclude_keys=[], exclude_properties=[]):
"""Returns the checksum of a db.Model.
Attributes:
ref: The reference og the db.Model
model: The model type instance of db.Model.
exclude_keys: To exclude a list of properties name like 'updated'
exclude_properties: To exclude list of properties type like 'db.DateTimeProperty'
Returns:
A checksum in signed integer.
"""
l = []
for key, prop in model.properties().iteritems():
if not (key in exclude_keys) and \
not any([True for x in exclude_properties if isinstance(prop, x)]):
l.append(getattr(ref, key))
return checksum_from_list(l)
def checksum_from_list(l):
"""Returns a checksum from a list of data into an int."""
return reduce(lambda x,y : x^y, [hash(repr(x)) for x in l])
注意:强>
对于base36实现:http://en.wikipedia.org/wiki/Base_36#Python_implementation
修改强> 我删除了base36中的返回,现在这些函数运行时没有依赖。 (来自@Skirmantas的建议)
答案 3 :(得分:1)
不适用于GAE,但在同样的情况下,我会使用类似的东西:
entity = Entity.get_by_key_name("foobar")
prev_entity_state = deepcopy(entity.__dict__)
if phase_of_moon() == "full":
entity.werewolf = True
if random.choice([True, False]):
entity.lucky = True
if some_complicated_condition:
entity.answer = 42
if entity.__dict__ == prev_entity_state:
entity.put()