覆盖子类中的dict.update()方法,以防止覆盖dict键 [英] Overriding dict.update() method in subclass to prevent overwriting dict keys
问题描述
今天早些时候,我阅读了提高错误蟒蛇字典理解覆盖了一个关键,并决定在我的手上回答。我自然发生的方法是为这个子类化 dict
。不过,我被困在我的答案上,现在我很痴迷于为自己而努力。
Earlier today, I read the question "Raise error if python dict comprehension overwrites a key" and decided to try my hand at an answer. The method that naturally occurred to me was to subclass dict
for this. However, I got stuck on my answer, and now I'm obsessed with getting this worked out for myself.
注意:
- 不 - 我没有计划把这个问题的答案转到另一个问题的答案。
- 这在这一点上纯粹是一个智力锻炼。实际上,我几乎肯定会使用一个
namedtuple
或一个常规字典,无论我有这样的要求。
- No - I do not plan on turning in the answer to this question as an answer to the other question.
- This is purely an intellectual exercise for me at this point. As a practical matter, I would almost certainly use a
namedtuple
or a regular dictionary wherever I have a requirement for something like this.
class DuplicateKeyError(KeyError):
pass
class UniqueKeyDict(dict):
def __init__(self, *args, **kwargs):
self.update(*args, **kwargs)
def __setitem__(self, key, value):
if key in self: # Validate key doesn't already exist.
raise DuplicateKeyError('Key \'{}\' already exists with value \'{}\'.'.format(key, self[key]))
super().__setitem__(key, value)
def update(self, *args, **kwargs):
if args:
if len(args) > 1:
raise TypeError('Update expected at most 1 arg. Got {}.'.format(len(args)))
else:
try:
for k, v in args[0]:
self.__setitem__(k, v)
except ValueError:
pass
for k in kwargs:
self.__setitem__(k, kwargs[k])
我的测试和预期结果
My Tests and Expected Results
>>> ukd = UniqueKeyDict((k, int(v)) for k, v in ('a1', 'b2', 'c3', 'd4')) # Should succeed.
>>> ukd['e'] = 5 # Should succeed.
>>> print(ukd)
{'a': 1, 'b': 2, 'c': 3, d: 4, 'e': 5}
>>> ukd['a'] = 5 # Should fail.
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 8, in __setitem__
__main__.DuplicateKeyError: Key 'a' already exists with value '1'.
>>> ukd.update({'a': 5}) # Should fail.
>>> ukd = UniqueKeyDict((k, v) for k, v in ('a1', 'b2', 'c3', 'd4', 'a5')) # Should fail.
>>>
我确定问题出现在我的 update()
方法,但我无法确定我在做错什么。
I'm certain the issue is in my update()
method, but I'm not able to determine just what I'm doing wrong.
以下是我的的原始版本update()
方法。对于已经在dict中的键/值对调用 my_dict.update({k:v})
时,此版本将失败,但不包括由于将args转换为 dict
会导致字典的默认行为,即重写重复键,因此重复键。 >
Below is the original version of my update()
method. This version fails as expected on duplicates when calling my_dict.update({k: v})
for a key/value pair already in the dict, but does not fail when including a duplicate key while creating the original dict, due to the fact that converting the args to a dict
results in default behavior for a dictionary, i.e., overwriting the duplicate key.
def update(self, *args, **kwargs):
for k, v in dict(*args, **kwargs).items():
self.__setitem__(k, v)
推荐答案
请注意,根据文档:
-
dict.update
单个其他
参数,另一个字典对象或键/值对的迭代(我已经使用c ollections.Mapping
来测试这一点)和如果指定了关键字参数,则使用这些键/值对更新字典;和 -
dict ()
采用单个映射
或可替换
以及可选的** kwargs
(与更新相同
接受...)。
dict.update
takes a singleother
parameter, "either another dictionary object or an iterable of key/value pairs" (I've usedcollections.Mapping
to test for this) and "If keyword arguments are specified, the dictionary is then updated with those key/value pairs"; anddict()
takes a singleMapping
orIterable
along with optional**kwargs
(the same asupdate
accepts...).
这不是你实现的接口,这是导致一些问题。我会实现如下:
This is not quite the interface you have implemented, which is leading to some issues. I would have implemented this as follows:
from collections import Mapping
class DuplicateKeyError(KeyError):
pass
class UniqueKeyDict(dict):
def __init__(self, other=None, **kwargs):
super().__init__()
self.update(other, **kwargs)
def __setitem__(self, key, value):
if key in self:
msg = 'key {!r} already exists with value {!r}'
raise DuplicateKeyError(msg.format(key, self[key]))
super().__setitem__(key, value)
def update(self, other=None, **kwargs):
if other is not None:
for k, v in other.items() if isinstance(other, Mapping) else other:
self[k] = v
for k, v in kwargs.items():
self[k] = v
正在使用:
>>> UniqueKeyDict((k, v) for k, v in ('a1', 'b2', 'c3', 'd4'))
{'c': '3', 'd': '4', 'a': '1', 'b': '2'}
>>> UniqueKeyDict((k, v) for k, v in ('a1', 'b2', 'c3', 'a4'))
Traceback (most recent call last):
File "<pyshell#8>", line 1, in <module>
UniqueKeyDict((k, v) for k, v in ('a1', 'b2', 'c3', 'a4'))
File "<pyshell#7>", line 5, in __init__
self.update(other, **kwargs)
File "<pyshell#7>", line 15, in update
self[k] = v
File "<pyshell#7>", line 10, in __setitem__
raise DuplicateKeyError(msg.format(key, self[key]))
DuplicateKeyError: "key 'a' already exists with value '1'"
和:
>>> ukd = UniqueKeyDict((k, v) for k, v in ('a1', 'b2', 'c3', 'd4'))
>>> ukd.update((k, v) for k, v in ('e5', 'f6')) # single Iterable
>>> ukd.update({'h': 8}, g='7') # single Mapping plus keyword args
>>> ukd
{'e': '5', 'f': '6', 'a': '1', 'd': '4', 'c': '3', 'h': 8, 'b': '2', 'g': '7'}
最终使用这个,我会倾向于给它一个不同的 __ repr __
以避免混淆!
If you ever end up using this, I'd be inclined to give it a different __repr__
to avoid confusion!
这篇关于覆盖子类中的dict.update()方法,以防止覆盖dict键的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!