【问题标题】:How to prevent key creation through d[key] = val如何防止通过 d[key] = val 创建密钥
【发布时间】:2015-11-22 09:16:48
【问题描述】:

假设我有d = {'dogs': 3}。使用:

d['cats'] = 2 

将创建键 'cats' 并为其赋予值 2

如果我真的打算用新的键和值更新字典,我会使用d.update(cats=2),因为它感觉更明确。

自动创建密钥感觉容易出错(尤其是在大型程序中),例如:

# I decide to make a change to my dict.
d = {'puppies': 4, 'big_dogs': 2}


# Lots and lots of code.
# ....

def change_my_dogs_to_maximum_room_capacity():
    # But I forgot to change this as well and there is no error to inform me.
    # Instead a bug was created.
    d['dogs'] = 1

问题:
有没有办法通过d[key] = value 禁用不存在的密钥的自动创建,而是提出KeyError

其他一切都应该继续工作:

d = new_dict()                  # Works
d = new_dict(hi=1)              # Works
d.update(c=5, x=2)              # Works
d.setdefault('9', 'something')  # Works

d['a_new_key'] = 1              # Raises KeyError

【问题讨论】:

  • 我猜你可以继承dict 并为相关的魔术方法编写一个自定义函数。
  • 你自相矛盾。你为什么不像你说的那样使用d.update(dogs=1)
  • @chepner 嗯,我想我还不够清楚。因为该函数不打算创建新键,而是更改旧键的值。但是由于自动插入而忘记更改功能会被忽视。
  • 不是您要求的,但如果键名由程序固定(不是从文件中读取),请考虑用自定义类替换您的字典。 __slots__ 声明修复了可以插入到此类对象中的属性名称。

标签: python dictionary key python-3.4


【解决方案1】:

您可以使用特殊的 __setitem__ 方法创建 dict 的子代,该方法拒绝最初创建时不存在的键:

class StrictDict(dict):
    def __setitem__(self, key, value):
        if key not in self:
            raise KeyError("{} is not a legal key of this StricDict".format(repr(key)))
        dict.__setitem__(self, key, value)

x = StrictDict({'puppies': 4, 'big_dogs': 2})
x["puppies"] = 23 #this works
x["dogs"] = 42    #this raises an exception

它并非完全无懈可击(例如,它允许x.update({"cats": 99}) 毫无怨言),但它可以防止最有可能发生的情况。

【讨论】:

  • @JoranBeasley It Shouldn't™,因为它继承了所有其他魔法方法。
  • 啊,看来你是对的......我发誓我用字符串做了这个,它破坏了我没有明确子类化的所有魔法方法......也许它只是因为字符串比 dict 或其他东西更原始
  • 这种方法为我提供了一个很好的解决方案,我在这里描述(对于 StackOverflow 来说太长了):persagen.com/2020/03/05/…
【解决方案2】:

这将只允许使用更新添加带有key=value 的新密钥:

 class MyDict(dict):
    def __init__(self, d):
        dict.__init__(self)
        self.instant = False
        self.update(d)

    def update(self, other=None, **kwargs):
        if other is not None:
            if isinstance(other, dict):
                for k, v in other.items():
                    self[k] = v
            else:
                for k, v in other:
                    self[k] = v
        else:
            dict.update(self, kwargs)
        self.instant = True

    def __setitem__(self, key, value):
        if self.instant and key not in self:
            raise KeyError(key)
        dict.__setitem__(self, key, value)

x = MyDict({1:2,2:3})
x[1] = 100 # works
x.update(cat=1) # works
x.update({2:200}) # works 
x["bar"] = 3 # error
x.update({"foo":2}) # error
x.update([(5,2),(3,4)])  # error

【讨论】:

    【解决方案3】:

    继承 dict 类并覆盖 __setitem__ 以满足您的需求。试试这个

    class mydict(dict):
        def __init__(self, *args, **kwargs):
            self.update(*args, **kwargs)
        def __setitem__(self, key, value):
            raise KeyError(key)
    
    >>>a=mydict({'a':3})
    >>>d
    {'a': 3}
    >>>d['a']
    3
    >>>d['b']=4
    KeyError: 'b'
    

    【讨论】:

      猜你喜欢
      • 2010-11-30
      • 2021-11-17
      • 1970-01-01
      • 1970-01-01
      • 2017-08-26
      • 2017-04-24
      • 2012-02-22
      • 2014-06-11
      • 2022-01-10
      相关资源
      最近更新 更多