问题:嵌套的defaultdict defaultdict
有没有办法使defaultdict也成为defaultdict的默认值?(即无限级递归defaultdict?)
我希望能够做到:
x = defaultdict(...stuff...)
x[0][1][0]
{}
因此,我可以做到x = defaultdict(defaultdict)
,但这仅是第二层:
x[0]
{}
x[0][0]
KeyError: 0
有一些食谱可以做到这一点。但是,仅使用常规的defaultdict参数就可以做到吗?
请注意,这是在问如何执行无限级递归defaultdict,因此它与Python不同:defaultdict的defaultdict?,这是执行两级defaultdict的方法。
我可能最终会使用束模式,但是当我意识到自己不知道该怎么做时,这引起了我的兴趣。
Is there a way to make a defaultdict also be the default for the defaultdict? (i.e. infinite-level recursive defaultdict?)
I want to be able to do:
x = defaultdict(...stuff...)
x[0][1][0]
{}
So, I can do x = defaultdict(defaultdict)
, but that’s only a second level:
x[0]
{}
x[0][0]
KeyError: 0
There are recipes that can do this. But can it be done simply just using the normal defaultdict arguments?
Note this is asking how to do an infinite-level recursive defaultdict, so it’s distinct to Python: defaultdict of defaultdict?, which was how to do a two-level defaultdict.
I’ll probably just end up using the bunch pattern, but when I realized I didn’t know how to do this, it got me interested.
回答 0
对于任意数量的级别:
def rec_dd():
return defaultdict(rec_dd)
>>> x = rec_dd()
>>> x['a']['b']['c']['d']
defaultdict(<function rec_dd at 0x7f0dcef81500>, {})
>>> print json.dumps(x)
{"a": {"b": {"c": {"d": {}}}}}
当然,您也可以使用lambda来执行此操作,但是我发现lambda的可读性较差。无论如何,它看起来像这样:
rec_dd = lambda: defaultdict(rec_dd)
For an arbitrary number of levels:
def rec_dd():
return defaultdict(rec_dd)
>>> x = rec_dd()
>>> x['a']['b']['c']['d']
defaultdict(<function rec_dd at 0x7f0dcef81500>, {})
>>> print json.dumps(x)
{"a": {"b": {"c": {"d": {}}}}}
Of course you could also do this with a lambda, but I find lambdas to be less readable. In any case it would look like this:
rec_dd = lambda: defaultdict(rec_dd)
回答 1
这里的其他答案告诉您如何创建一个defaultdict
包含“无限多个”的defaultdict
,但是它们无法解决我认为您最初的需求,即仅具有两个深度的defaultdict。
您可能一直在寻找:
defaultdict(lambda: defaultdict(dict))
您可能更喜欢此构造的原因是:
- 它比递归解决方案更明确,因此读者可能更容易理解。
- 这使的“叶”
defaultdict
可以是除字典之外的其他内容,例如:defaultdict(lambda: defaultdict(list))
或defaultdict(lambda: defaultdict(set))
The other answers here tell you how to create a defaultdict
which contains “infinitely many” defaultdict
, but they fail to address what I think may have been your initial need which was to simply have a two-depth defaultdict.
You may have been looking for:
defaultdict(lambda: defaultdict(dict))
The reasons why you might prefer this construct are:
- It is more explicit than the recursive solution, and therefore likely more understandable to the reader.
- This enables the “leaf” of the
defaultdict
to be something other than a dictionary, e.g.,: defaultdict(lambda: defaultdict(list))
or defaultdict(lambda: defaultdict(set))
回答 2
有一个不错的技巧:
tree = lambda: defaultdict(tree)
然后,您可以使用创建自己x
的x = tree()
。
There is a nifty trick for doing that:
tree = lambda: defaultdict(tree)
Then you can create your x
with x = tree()
.
回答 3
与BrenBarn的解决方案类似,但是不包含tree
两次变量名,因此即使更改了变量字典也可以使用:
tree = (lambda f: f(f))(lambda a: (lambda: defaultdict(a(a))))
然后,您可以创建的每个新x
用x = tree()
。
对于该def
版本,我们可以使用函数闭包作用域来保护数据结构,以免其tree
名称被反弹时现有实例停止工作的缺陷。看起来像这样:
from collections import defaultdict
def tree():
def the_tree():
return defaultdict(the_tree)
return the_tree()
Similar to BrenBarn’s solution, but doesn’t contain the name of the variable tree
twice, so it works even after changes to the variable dictionary:
tree = (lambda f: f(f))(lambda a: (lambda: defaultdict(a(a))))
Then you can create each new x
with x = tree()
.
For the def
version, we can use function closure scope to protect the data structure from the flaw where existing instances stop working if the tree
name is rebound. It looks like this:
from collections import defaultdict
def tree():
def the_tree():
return defaultdict(the_tree)
return the_tree()
回答 4
我还将提出更多OOP样式的实现,该实现支持无限嵌套以及正确格式化repr
。
class NestedDefaultDict(defaultdict):
def __init__(self, *args, **kwargs):
super(NestedDefaultDict, self).__init__(NestedDefaultDict, *args, **kwargs)
def __repr__(self):
return repr(dict(self))
用法:
my_dict = NestedDefaultDict()
my_dict['a']['b'] = 1
my_dict['a']['c']['d'] = 2
my_dict['b']
print(my_dict) # {'a': {'b': 1, 'c': {'d': 2}}, 'b': {}}
I would also propose more OOP-styled implementation, which supports infinite nesting as well as properly formatted repr
.
class NestedDefaultDict(defaultdict):
def __init__(self, *args, **kwargs):
super(NestedDefaultDict, self).__init__(NestedDefaultDict, *args, **kwargs)
def __repr__(self):
return repr(dict(self))
Usage:
my_dict = NestedDefaultDict()
my_dict['a']['b'] = 1
my_dict['a']['c']['d'] = 2
my_dict['b']
print(my_dict) # {'a': {'b': 1, 'c': {'d': 2}}, 'b': {}}
回答 5
这是一个递归函数,用于将递归默认字典转换为普通字典
def defdict_to_dict(defdict, finaldict):
# pass in an empty dict for finaldict
for k, v in defdict.items():
if isinstance(v, defaultdict):
# new level created and that is the new value
finaldict[k] = defdict_to_dict(v, {})
else:
finaldict[k] = v
return finaldict
defdict_to_dict(my_rec_default_dict, {})
here is a recursive function to convert a recursive default dict to a normal dict
def defdict_to_dict(defdict, finaldict):
# pass in an empty dict for finaldict
for k, v in defdict.items():
if isinstance(v, defaultdict):
# new level created and that is the new value
finaldict[k] = defdict_to_dict(v, {})
else:
finaldict[k] = v
return finaldict
defdict_to_dict(my_rec_default_dict, {})
回答 6
我在这里基于安德鲁的答案。如果要从json或现有字典将数据加载到嵌套程序defaultdict中,请参见以下示例:
def nested_defaultdict(existing=None, **kwargs):
if existing is None:
existing = {}
if not isinstance(existing, dict):
return existing
existing = {key: nested_defaultdict(val) for key, val in existing.items()}
return defaultdict(nested_defaultdict, existing, **kwargs)
https://gist.github.com/nucklehead/2d29628bb49115f3c30e78c071207775
I based this of Andrew’s answer here.
If you are looking to load data from a json or an existing dict into the nester defaultdict see this example:
def nested_defaultdict(existing=None, **kwargs):
if existing is None:
existing = {}
if not isinstance(existing, dict):
return existing
existing = {key: nested_defaultdict(val) for key, val in existing.items()}
return defaultdict(nested_defaultdict, existing, **kwargs)
https://gist.github.com/nucklehead/2d29628bb49115f3c30e78c071207775