【问题标题】:Initialize non existing keys in OrderedDict with default values like defaultDict使用 defaultDict 等默认值初始化 OrderedDict 中不存在的键
【发布时间】:2021-12-30 01:13:13
【问题描述】:

有什么方法可以使用默认值访问 OrderedDict 中不存在的键,就像我们在 defaultDict 中所做的那样

Ex.
od = OrderedDict({'a':1})
print(od['b'])  # Output: KeyError: 'b'

这是KeyError在下面的实现中抛出的真正问题

l =['a','b','c','b']
od = OrderedDict({})

for k in l:
    od[k] += 1 # KeyError

有意避免这种实现

for k in l:
    if k in od:
        od[k] += 1
    else:
        od[k] = 1

【问题讨论】:

  • 这能回答你的问题吗? How to implement an ordered, default dict?
  • @Arne,提供的链接是关于新的自定义字典类型的实现。如果OrderedDict 中有任何可用的功能,或者在其他库中是否有类似于OrderedDict 的任何其他可用类型,我正在尝试直接使用库中的功能
  • @JanPo 然后使用 defaultdict。它维护其插入顺序。你不需要 OrderedDict。
  • 我明白这一点。我只是说,如果您需要 OrderedDict 的原因是为了维护密钥顺序,那么您实际上根本不需要它,因为 defaultdict 现在可以自己完成这一切(普通的 ol' dict 也是如此)。还是您需要专门使用 OrderedDict 的其他原因?
  • 换句话说,如果您解释为什么需要 OrderedDict 而不是 defaultdict,我们可能会更好地为您提供帮助。

标签: python python-3.x


【解决方案1】:

只需从OrderedDict继承并重新定义__getitem__

from collections import OrderedDict


class OrderedDictFailFree(OrderedDict):
    def __getitem__(self, name):
        try:
            return OrderedDict.__getitem__(self, name)
        except KeyError:
            return None


od = OrderedDictFailFree({'a': 1})
print(od['b'])

【讨论】:

  • 如果您还重新定义__init__ 以接受默认值(或可调用,以复制 defaultdict 的工作方式)怎么办?
  • 如果你手动实现,正确的方法是定义__missing__而不是覆盖__getitem__。您可以完全删除__getitem__ 的定义并定义def __missing__(self, key): return None,它的行为与上述完全相同,除了对于已存在键的查找更有效地显着
【解决方案2】:

这当然是可行的,正如Yevgeniy Kosmak's answer 所示。但是,请记住,在 Python >=3.7(或者甚至 >=3.6,只要您使用默认的 CPython 实现)中,所有字典现在都是 maintain insertion order。因此,如果您不需要与旧版本兼容,并且您唯一需要的是保持顺序的密钥,那么您很可能根本不需要OrderedDictdefaultdict 也将保持其秩序。

但是,OrderedDictdict/defaultdict 之间仍然存在 some differences,这可能会或可能不会影响您的用例。其中包括:

  • 两个OrderedDicts 之间的平等检查不仅检查键/值,还检查顺序。 (这不适用于两个 defaultdicts,或者一个 OrderedDict 与一个 defaultdict 核对。)
  • OrderedDictmove_to_end() 方法可以有效地将键/值对移动到前端或末尾。
  • OrderedDictpopitem() 方法可以选择性地弹出并返回 first 键/值而不是最后一个。 (dictpopitem() 返回最后插入的键/值——或者,在 3.7 之前,任意对。)
  • 在 3.8 之前,dict/defaultdict 不直接支持反向迭代,例如 reversed()。但是,在 3.6 或 3.7 you can still achieve this 中带有中间列表或元组。

注意:即使dict 现在复制了OrderedDict 的所有功能,后者仍可能不会从 Python 中删除。现有的旧程序已经使用 OrderedDict,并且 Python 通常会尝试不使用新的次要版本(例如 3.6 到 3.7)破坏现有代码,除非有令人信服的原因,例如在语法中添加新关键字。有关 Python 版本控制系统的更多信息,请参阅 here,有关向后兼容性的详细信息,请参阅 here

【讨论】:

  • 3.有人可能想要OrderedDict 仍然拥有的优势。
  • 查看its documentation。请注意这两种方法及其参数,以及那里提到的顺序敏感比较。我想我不久前也将OrderedDict 用于它的popitem(False),因为它比使用常规dict 执行等效操作要得多。
  • @KellyBundy 谢谢!我不敢相信我以某种方式错过了它。我已经编辑了我的答案,并学到了新的东西。
【解决方案3】:

看起来我们可以使用od.setdefault('b',0),虽然它不是那么优雅的解决方案。

od = OrderedDict({'a':1})
print(od.setdefault('b',0)) # od.get('b',0) could be used if intention is not to change od
l =['a','b','c','b','b']
od = OrderedDict({})

for k in l:   
    od[k] = od.setdefault(k,0) + 1 # set key to 0 if there in no key k and increment by 1

【讨论】:

  • 你可以只做od[k] = od.setdefault(k, 0) + 1(或od[k] = od.get(k, 0) + 1在这种情况下具有同等效果); setdefault 返回现有值(或之前未设置时新设置的默认值),因此可以内联。
  • 这是个好建议。我会相应地编辑
猜你喜欢
  • 2022-01-14
  • 2017-11-22
  • 1970-01-01
  • 2014-12-29
  • 2017-12-18
  • 1970-01-01
  • 1970-01-01
  • 2020-02-08
  • 2013-01-17
相关资源
最近更新 更多