逻辑上合并物理上不同的词典

时间:2012-01-21 20:45:04

标签: python performance python-3.x

我正在尝试围绕两个字典编写一个包装器,这样它们看起来像一个字典(仅用于阅读;写入应该引发异常)。

我这样做是为了节省内存,因为其他地方需要一个原始字典。我认为它比合并字典更快,如果组合字典中的元素不到一半将会被查找。

这是我的尝试:

class LogicalMerge:
  def __init__(self, d1, d2):
    #d1 and d2 are dictionaries
    self.d1 = d1
    self.d2 = d2
  def __getitem__(self, x):
    if x in self.d1:
      return self.d1[x]
    else:
      return self.d2[x]

d1 = {1:2, 3:4}
d2 = {5:10}
d = LogicalMerge(d1, d2)
d[1] # == 2
d[5] # == 10

此方法是否存在任何设计,技术或性能问题?

2 个答案:

答案 0 :(得分:4)

通过将__getitem__重写为

,您可以为每次通话节省一次查询
try:
    return self.d1[x]
except KeyError:
    return self.d2[x]

这遵循了"请求宽恕,而不是许可"的pythonic成语。

  

我也认为它比合并词典更快

我强烈怀疑,但你应该确定。您的方法引入了额外的间接级别,并且需要对密钥进行多次哈希处理。它也必然需要更多的记忆。

修改:这是另一种方法。使用DictWithBackup替换您的某个词组,其行为类似于dict,但是当缺少某个键时,它会查看另一个dict

class DictWithBackup(dict):
    def __init__(self, backup):
         self._backup = backup

    def __missing__(self, key):
         return self._backup[key]

此版本可避免异常处理。

答案 1 :(得分:3)

出于性能原因,我更喜欢以下内容。给定None是一个永远不会有效发生的对象。

def __getitem__(self, k):
  v = self.d1.get(k, None)
  if v is None:
    v = self.d2[k] # if you're going to raise an error anyway ...
  return v

否则您可以默认获取自定义对象。注意,您需要一个实现__eq__的对象来测试值相等(o1 == o2),或者---更好的性能---您使用不可变对象,即某个字符串"error_key_not_found_string",每次都不是新创建的。然后,您甚至可以按对象标识id(o1) == id(o2)进行比较,即使用is运算符。 (您不需要提供__eq__。)

def __getitem__(self, k):
  v = self.d1.get(k, "error_key_not_found_string")
  # if id(v) == id("error_key_not_found_string":
  if v is "error_key_not_found_string": 
    v = self.d2[k] # if you're going to raise an error anyway ...
  return v

你有没有想过这个案例,这个项目在两个字典中?

总之,从设计角度来看,我觉得这有点令人困惑。性能增益是否真的证明了额外的错误和混淆源?另外,您将失去所有其他dict功能...... 这可以像d1.update(d2)一样简单。鉴于d1是您不在其他地方使用的字典(您可以使用deepcopy)。