问题
实验楼python挑战赛1 实现一个不可变的dict,数据只能由类初始化的时候通过参数传递,修改、添加都会抛出TypeError
解决方法
继承ABCs中的MultiMapping, 复写其中的部分方法即可。
代码
import collections
class ImmutableDict(collections.MutableMapping):
def __init__(self, **kwargs):
self.store = dict(**kwargs)
self.error = TypeError("'ImmutableDict' objects are immutable")
# self.update(dict(*args, **kwargs))
def __setitem__(self, key, value):
# 涉及到修改时会触发这个方法
raise self.error
def __iter__(self):
return iter(self.store)
def __delitem__(self, key):
# 删除时触发
raise self.error
def __getitem__(self, key):
return self.store[key]
def __len__(self):
return len(self.store)
class Get(object):
def __init__(self):
pass
def __getitem__(self, item):
return hash(item)
if __name__ == "__main__":
test = ImmutableDict(name="sun", age=22, location="China")
# test["name"] = "zhang"
# test.pop("name") TypeError will raised..
# print(test.pop("name"))
# for item in test:
# print(item)
#print(test[item])
MultiMapping 源码:
class MutableMapping(Mapping):
@abstractmethod
def __setitem__(self, key, value):
raise KeyError
@abstractmethod
def __delitem__(self, key):
raise KeyError
__marker = object()
def pop(self, key, default=__marker):
try:
value = self[key]
except KeyError:
if default is self.__marker:
raise
return default
else:
del self[key]
return value
def popitem(self):
try:
key = next(iter(self))
except StopIteration:
raise KeyError
value = self[key]
del self[key]
return key, value
def clear(self):
try:
while True:
self.popitem()
except KeyError:
pass
def update(self, other=(), **kwds):
if isinstance(other, Mapping):
for key in other:
self[key] = other[key]
elif hasattr(other, "keys"):
for key in other.keys():
self[key] = other[key]
else:
for key, value in other:
self[key] = value
for key, value in kwds.items():
self[key] = value
def setdefault(self, key, default=None):
try:
return self[key]
except KeyError:
self[key] = default
return default
MutableMapping.register(dict)
Mapping 源码:
class Mapping(Sized, Iterable, Container):
@abstractmethod
def __getitem__(self, key):
raise KeyError
def get(self, key, default=None):
try:
return self[key]
except KeyError:
return default
def __contains__(self, key):
try:
self[key]
except KeyError:
return False
else:
return True
def iterkeys(self):
return iter(self)
def itervalues(self):
for key in self:
yield self[key]
def iteritems(self):
for key in self:
yield (key, self[key])
def keys(self):
return list(self)
def items(self):
return [(key, self[key]) for key in self]
def values(self):
return [self[key] for key in self]
# Mappings are not hashable by default, but subclasses can change this
__hash__ = None
def __eq__(self, other):
if not isinstance(other, Mapping):
return NotImplemented
return dict(self.items()) == dict(other.items())
def __ne__(self, other):
return not (self == other)
分析两个问题:
- .get 的时候究竟发生了什么?
- .pop 的时候发生了什么?
尝试进行断点调试:
get 跳转到了Mapping中的get
注意其中的self就是我们实例化的immutableDict类
结论: .get显示定义是在Mapping中,但是Mapping又把这个方法的实现抛给了子类,事实上,就是调用了 immutableDict.getitem中的方法。
所以我们在get 会触发我们在 immutableDict.getitem中定义的异常
pop 也就很类似了。