您将遇到的问题是,为了在嵌套字典上设置项目,您必须首先能够获取所有父项目。例如:
d[1][2][3] = 42
需要得到 d[1][2]
才能设置 d[1][2][3]
。当您访问中间字典时,无法知道分配是否正在进行中,因此使分配工作的唯一方法是始终在访问时创建子字典。(您可以返回某种代理对象而不是创建子字典,并将中间字典的创建推迟到分配,但是当您访问不存在的路径时仍然不会出错。)
解决此问题的最简单方法是使用单个元组键而不是重复的子键。换句话说,d[1][2][3]
您将设置而不是设置d[1, 2, 3]
。赋值是独立的操作:它们不需要获取任何中间嵌套级别,因此您可以仅在赋值时创建中间级别。
作为奖励,您可能会发现在传递多个键时使用元组更加简单,因为您可以将它们插入[]
并获得您想要的项目。
您可以使用单个字典来执行此操作,使用元组作为键。但是,这会丢失数据的层次结构。下面的实现使用子字典。使用了一个字典子类node
,以便我们可以在字典上分配一个属性来表示该位置节点的值;这样,我们可以在中间节点和叶子上存储值。(它有一个__repr__
显示节点值及其子节点的方法,如果有的话。)类的__setitem__
方法tupledict
在分配元素时处理创建中间节点。__getitem__
遍历节点以找到您想要的值。(如果要将单个节点作为节点访问,可以使用get()
一次接一个。)
class tupledict(dict):
class node(dict):
def __repr__(self):
if self:
if hasattr(self, "value"):
return repr(self.value) + ", " + dict.__repr__(self)
return dict.__repr__(self)
else:
return repr(self.value)
def __init__(self):
pass
def __setitem__(self, key, value):
if not isinstance(key, tuple): # handle single value
key = [key]
d = self
for k in key:
if k not in d:
dict.__setitem__(d, k, self.node())
d = dict.__getitem__(d, k)
d.value = value
def __getitem__(self, key):
if not isinstance(key, tuple):
key = [key]
d = self
for k in key:
try:
d = dict.__getitem__(d, k)
except KeyError:
raise KeyError(key[0] if len(key) == 1 else key)
try:
return d.value
except AttributeError:
raise KeyError(key[0] if len(key) == 1 else key)
用法:
td = tupledict()
td['foo', 'bar'] = 'spam'
td['foo', 'eggs'] # KeyError
key = 'foo', 'bar'
td[key] # 'spam'