Dou*_* R. 15 python dictionary
今天早些时候,我读到了一个问题" 如果python dict理解覆盖了一个键就会引发错误 ",并决定试着回答一下.我自然发生的方法是为此子类化dict.但是,我坚持了我的答案,现在我痴迷于为自己解决这个问题.
笔记:
namedtuple或一个常规字典,只要我有这样的要求.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])
Run Code Online (Sandbox Code Playgroud)
>>> 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.
>>>
Run Code Online (Sandbox Code Playgroud)
我确定问题出在我的update()方法中,但我无法确定我做错了什么.
以下是我的update()方法的原始版本.在调用my_dict.update({k: v})dict中已有的键/值对时,此版本在重复项上按预期失败,但在创建原始dict时包含重复键时不会失败,因为将args转换为dict默认行为的结果字典,即覆盖重复的密钥.
def update(self, *args, **kwargs):
for k, v in dict(*args, **kwargs).items():
self.__setitem__(k, v)
Run Code Online (Sandbox Code Playgroud)
请注意,根据文档:
dict.update接受一个other参数,“另一个字典对象或一个可迭代的键/值对”(我曾经collections.Mapping为此进行过测试)和“如果指定了关键字参数,则字典将使用这些键/值对进行更新”;和dict()需要单个Mapping或Iterable与可选**kwargs(与update接受...相同)。这不是您实现的接口,这会导致一些问题。我会按如下方式实施:
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
Run Code Online (Sandbox Code Playgroud)
正在使用:
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
Run Code Online (Sandbox Code Playgroud)
和:
>>> 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'}
Run Code Online (Sandbox Code Playgroud)
如果你最终使用它,我会倾向于给它一个不同的__repr__以避免混淆!
有趣的是,简单地覆盖__setitem__不足以改变updatein 的行为dict.我原以为dict在使用它__setitem__时会使用它的方法update.在所有情况下,我认为最好在collections.MutableMapping不触及的情况下实现所需的结果update:
import collections
class UniqueKeyDict(collections.MutableMapping, dict):
def __init__(self, *args, **kwargs):
self._dict = dict(*args, **kwargs)
def __getitem__(self, key):
return self._dict[key]
def __setitem__(self, key, value):
if key in self:
raise DuplicateKeyError("Key '{}' already exists with value '{}'.".format(key, self[key]))
self._dict[key] = value
def __delitem__(self, key):
del self._dict[key]
def __iter__(self):
return iter(self._dict)
def __len__(self):
return len(self._dict)
Run Code Online (Sandbox Code Playgroud)
编辑:dict作为基类包含以满足isinstance(x, dict)检查.