这个问题是关于在Python中实现完整的Perl自动修改。我知道之前有类似的问题,到目前为止最好的答案是“What is the best way to implement nested dictionaries in Python?”。但是,我希望这样做:
a['x']['y'].append('z')
首先声明a['x']['y'] = []
,或者更确切地说,不声明a['x'] = {}
。 (请注意,在Perl中,您可以执行push @{$a->{x}{y}}, 'z';
。)
我知道dict
和list
类没有混合,所以这很难,但我很想知道某人是否有一个巧妙的解决方案可能涉及从{{创建一个继承的类1}}但是在它上面定义了一个新的dict
方法?
我也知道这可能会让一些Python纯粹主义者想要我坚持使用Perl。但是,即使只是为了挑战,我也希望看到一些东西。
答案 0 :(得分:16)
a = collections.defaultdict(lambda: collections.defaultdict(list))
答案 1 :(得分:2)
也许这可以解决您对词典中任意数量“维度”的需求:
a= collections.defaultdict(list)
代码中唯一的变化是:
a['x', 'y'].append('z')
当然,这个解决方案就是你想要的解决方案取决于两个条件:
如果这两个条件中的任何一个都成立,我的解决方案将无法帮助您。
答案 2 :(得分:2)
由于我们事先并不知道我们是否需要字典或列表,因此您无法将自动生成与列表相结合。除非,根据链接问题解决Nosklo的回答,你要添加列表"功能"到底层字典。基本上假设"排序"键的顺序,并始终使用列表方法。我已经做了这个例子:
class AutoVivification(dict):
"""Implementation of perl's autovivification feature. Has features from both dicts and lists,
dynamically generates new subitems as needed, and allows for working (somewhat) as a basic type.
"""
def __getitem__(self, item):
if isinstance(item, slice):
d = AutoVivification()
items = sorted(self.iteritems(), reverse=True)
k,v = items.pop(0)
while 1:
if (item.start < k < item.stop):
d[k] = v
elif k > item.stop:
break
if item.step:
for x in range(item.step):
k,v = items.pop(0)
else:
k,v = items.pop(0)
return d
try:
return dict.__getitem__(self, item)
except KeyError:
value = self[item] = type(self)()
return value
def __add__(self, other):
"""If attempting addition, use our length as the 'value'."""
return len(self) + other
def __radd__(self, other):
"""If the other type does not support addition with us, this addition method will be tried."""
return len(self) + other
def append(self, item):
"""Add the item to the dict, giving it a higher integer key than any currently in use."""
largestKey = sorted(self.keys())[-1]
if isinstance(largestKey, str):
self.__setitem__(0, item)
elif isinstance(largestKey, int):
self.__setitem__(largestKey+1, item)
def count(self, item):
"""Count the number of keys with the specified item."""
return sum([1 for x in self.items() if x == item])
def __eq__(self, other):
"""od.__eq__(y) <==> od==y. Comparison to another AV is order-sensitive
while comparison to a regular mapping is order-insensitive. """
if isinstance(other, AutoVivification):
return len(self)==len(other) and self.items() == other.items()
return dict.__eq__(self, other)
def __ne__(self, other):
"""od.__ne__(y) <==> od!=y"""
return not self == other
这遵循为dud键动态生成自身的基本自动修复功能。但是,它还实现了一些方法listed here。这使它像一个准列表/字典的行为。
对于列表功能的其余部分,请添加列出的方法。我用列表方法将其视为字典。如果调用了一个列表方法,那么它会假设所持有的项目的顺序,即字符串排序低于整数,并且这些键总是在&#34;排序&#34;顺序。
它还支持添加these methods的示例。这来自我自己的用例。我需要从AutoVivified字典中添加项目,但如果它不存在,则创建并返回一个新的AutoVivification
对象。它们没有整数&#34;值&#34;所以你不能这样做:
rp = AutoVivification()
rp['a']['b'] = 3
rp['a']['b'] + rp['q']
这打败了目的,因为我不知道是否会有某些东西存在,但我还是想要默认。所以我已经添加了__add__
和__radd__
方法。它们使用基础字典的length
作为integer
值,因此新创建的AV对象的值为零以进行添加。如果一个键除了AV对象之外还有其它东西,那么我们就会得到那个东西的加法方法。
答案 3 :(得分:1)
只是扩展Ignacio的答案,提出一些额外的选项,允许您从Python词典中明确请求更多神奇的行为。以这种方式编写的代码的可维护性仍然是可疑的,但我想清楚地表明这是一个“这种数据结构是否可维护?”的问题。 (我有疑虑)不是“可以让Python以这种方式行事吗?” (当然可以)。
为了支持命名空间方面的任意级别的嵌套,您所要做的就是命名函数(而不是使用lambda)并使其自引用:
>>> from collections import defaultdict
>>> def autodict(): return defaultdict(autodict)
...
>>> a = autodict()
>>> a[1][2][3] = []
>>> type(a[1])
<class 'collections.defaultdict'>
>>> type(a[1][2])
<class 'collections.defaultdict'>
>>> type(a[1][2][3])
<class 'list'>
>>> a[1][2][3]
[]
但是,这会引入“问题”,您必须先明确设置列表,然后才能追加它。 Python的答案在于setdefault
方法,它实际上比collections.defaultdict
更长:
>>> a.setdefault(3, []).append(10)
>>> a.setdefault(3, []).append(11)
>>> a[3]
[10, 11]
>>> a[2].setdefault(3, []).append(12)
>>> a[2].setdefault(3, []).append(13)
>>> a[2][3]
[12, 13]
>>> a[1][2].setdefault(3, []).append(14)
>>> a[1][2].setdefault(3, []).append(15)
>>> a[1][2][3]
[14, 15]
所有collections.defaultdict
实际上都是通常情况下,您总是将相同的第二个参数传递给dict.setdefault
更容易使用。对于更复杂的情况,例如此情况,您仍然可以直接使用dict.setdefault
来处理collections.defaultdict
无法处理的方面。