可以将 dict.get() 嵌套在另一个内部,还是这种糟糕的设计?

Is it okay to nest a dict.get() inside another or is this bad design?

因此,我正在处理一个代码库,其中字典包含一些关键信息。在开发过程中的某个时刻,其中一个键的名称发生了变化,但旧键在很多地方仍然存在。让我们调用键 newold 以供参考。

为了兼容旧版本,我正在做这样的事情:

dict_name.get(new_key,dict_name.get(old_key,None))

这个设计不好还是没问题? Why/Why 不是吗?

说明示例:(基于@Alexander 的输入)

有两个词典d1和d2。

d1={k1:v1,old_key:some_value}
d2={k1:v1,new_key:some_value}

我现在正在设计的函数可以像字典一样获取 d1 或 d2 作为参数。我的函数应该能够获取 some_value,无论是否存在 old_key 或 new_key。

dict.get 正是出于这个原因,因此如果键不在其中,您可以使用默认值。

有双重后备非常好。例如:

d = {}
result = d.get('new_key',d.get('old_key', None))

这意味着 result 在最坏的情况下是 None,但没有错误(这首先是 get 的目标。

换句话说,它会优先获得new_key的值,old_key是第二优先,None是第三。

还值得注意的是 get(key, None)get(key) 相同,因此您可能希望缩短该行:

result = d.get('new_key', d.get('old_key'))

如果你想避免多次调用 get(例如,如果你必须调用其中的 2 个以上,它将不可读)你可以这样做:

priority = ('new_key', 'old_key', 'older_key', 'oldest_key')
for key in priority:
    result = d.get(key)
    if result is not None:
        break

并且 result 成为该循环中首先遇到的任何内容,或者 None 否则

这是一个合理的方法。唯一的缺点是它会对两个键执行get,这在大多数情况下不会影响性能。

我唯一的笔记是挑剔的:

  • dict为保留字,请勿作为变量使用
  • None是默认的,所以可以去掉old_key,例如:
info.get('a', info.get('b'))

回应"Is there a way to prevent the double call?":是的,存在几种合理的方法=)。

  1. 单行线可能看起来像:

    info['a'] if 'a' in info else info.get('b')

    如果你的键更长,它开始变得难以阅读。

  2. 更详细的方法是将其扩展为完整的语句:

    val = None
    if 'a' in info:
        val = info['a']
    elif 'b' in info:
        val = info['b']
    
  3. 最后一个通用选项(*keys 之后的默认选项)将仅适用于 python 3):

    def multiget(info, *keys, default=None):
        ''' Try multiple keys in order, or default if not present '''
        for k in keys:
            if k in info:
                return info[k]
        return default
    

    这将让您干净地解决多个调用,例如:

    option_1 = multiget(info, 'a', 'b')
    option_2 = multiget(info, 'x', 'y', 'z', default=10)
    

如果这在某种程度上是多个 api 版本的流行病或某种东西(?)你甚至可以包装 dict,尽管它可能有点矫枉过正:

>>> class MultiGetDict(dict):
...   def multiget(self, *keys, default=None):
...       for k in keys:
...           if k in self:
...               return self[k]
...       return default
... 
>>> d = MultiGetDict({1: 2})
>>> d.multiget(1)
2
>>> d.multiget(0, 1)
2
>>> d.multiget(0, 2)
>>> d.multiget(0, 2, default=3)
3

根据提供的示例词典,我认为这是糟糕的设计...

假设您的原始词典是:

d1 = {'k1': 1, 'k2': 2}

如果我没理解错的话,你就'update'其中一个键,例如:

d1 = {'k3': 1, 'k2': 2}

如果您尝试通过以下方式访问:

d1.get('k3', d1.get('k1'))  # 'k3' is new key, 'k1' is old key.

那么第一个查找将始终存在,第二个查找将永远不会被使用。

如果您的意思是新词典看起来像:

d2 = {'k1': 1, 'k2': 2, 'k3': 1}

那么您将 'same' 数据存储在字典中的两个不同位置,这肯定会导致麻烦(类似于数据库中的规范化数据)。例如,如果 'k3' 的值更新为 3,那么 k1 的值也需要更新。

鉴于您的示例中提供的词典:

d1={k1: v1, old_key: some_value}
d2={k1: v1, new_key: some_value}

我假设 some_value 在两者中都是相等的,即 d1[old_key] == d2[new_key]。如果是这样,那么您 可以 使用 d2.get(new_key, d1.get(old_key)。然而,它看起来就像一团糟。

  • 如果some_value需要更新,例如,必须在两个词典中更新。
  • 您存储 some_value 两次是在浪费内存。
  • 您在 d2 中的 new_key 可能会意外破坏 d1 中的现有密钥。

我建议首先不要更改键名。