我在实现一个使用字典来存储数据的程序。除了普通数据之外,它还存储一些内部数据,所有内部数据都以 _ 为前缀。但是,我希望将库的用户与这些数据隔离开来,因为他们通常不关心这些数据。此外,我需要在我的类中设置一个修改标记,以跟踪数据是否被修改。
对于所有界面函数,这种方法都能很好地工作,这里有两个示例,一个带修改,另一个不带修改。请注意,在这种情况下,我没有隐藏内部数据,因为它是故意作为键而要求的:
def __getitem__(self, key):
return self._data[key]
def __setitem__(self, key, value):
self.modified = True
self._data[key] = value
在一些函数中,例如 iter,我会在产生数据之前过滤掉所有以 _ 开头的数据。
但是有一个函数在这里造成了真正的问题:popitem。在它的正常行为中,它只会撤回一个任意项并将其返回,同时从字典中删除它。然而,问题来了:如果没有深入的内部知识,我事先不知道会返回哪一项。但我知道 popitem 遵循与 items 和 keys 相同的规则。所以我想到一个实现方法:
keys = self._data.keys()
for k in keys:
if k.startswith("_"):
continue
v = self._data.pop(k)
self.modified = True
return k, v
else:
raise KeyError('popitem(): dictionary is empty')
这种实现方法确实可行。但它感觉不符合 Python 的惯例,而且一点也不动态或干净。它也与引发异常的方式作斗争,就像这样:{}.popitem(),这看起来完全疯狂,但至少会给我一种动态的方式(例如,如果异常消息或类型发生变化,我就不必去调整)。
我现在正在寻找一个更干净、更不疯狂的方法来解决这个问题。有一种方法可以从字典中删除内部数据,但我只会把它作为最后的手段。所以你有任何解决这个问题的食谱或想法吗?
解决方案
答案1:
给你的对象两个字典属性:self._data 和 self._internal_data。然后将所有字典方法转发到 self._data,你就不必过滤任何内容了。
class MyDict(dict):
def __init__(self, data, internal_data):
self._data = data
self._internal_data = internal_data
def __getitem__(self, key):
return self._data[key]
def __setitem__(self, key, value):
self.modified = True
self._data[key] = value
# ... and so on for other dict methods
答案2:
继承 dict 而不是包装字典。你将需要实现少得多的东西。
将你的“内部数据”存储为对象上的属性,而不是存储在字典中。这样,如果需要,很容易就能访问它们,但它们不会出现在普通的迭代中。如果你在某个时候需要组合它们,可以使用 x = dict(self); x.update(self.dict) 来创建一个包含两组值的新字典。
如果你确实想将你的内部数据存储为字典,那么就把它嵌入进去。在你主要的对象上实现 missing,这样你就可以从内部字典中抓取项目,如果它们在主要字典中找不到的话。
class MyDict(dict):
def __init__(self, data, internal_data):
self._data = data
self._internal_data = internal_data
def __getitem__(self, key):
try:
return self._data[key]
except KeyError:
return self._internal_data[key]
def __setitem__(self, key, value):
self.modified = True
self._data[key] = value
# ... and so on for other dict methods