如何进行高级Python哈希自动维生?
这个问题是关于如何在Python中实现完整的Perl自动生成(autovivification)功能。我知道之前有类似的问题被问过,目前最好的答案在这个链接里:在Python中实现嵌套字典的最佳方法是什么?。不过,我想做的是:
a['x']['y'].append('z')
不需要先声明 a['x']['y'] = []
,或者说,也不需要先声明 a['x'] = {}
。 (注意在Perl中,你可以这样做 push @{$a->{x}{y}}, 'z';
。)
我知道 dict
和 list
这两个类有点不太搭,所以这很难,但我想看看有没有人能想出一个聪明的解决方案,可能是通过创建一个继承自 dict
的类,并在上面定义一个新的 append
方法?
我也知道这可能会让一些Python的纯粹主义者不高兴,他们会让我还是用Perl。但就算只是为了挑战一下,我也想看看有没有什么办法。
5 个回答
因为我们事先不知道是需要字典还是列表,所以你不能把自动生成(autovivification)和列表结合起来。除非像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
这个示例遵循了基本的自动生成特性,可以为不存在的键动态生成内容。不过,它也实现了一些这里列出的方法。这让它可以像一个半列表半字典的东西。
对于其他列表特性,可以添加列出的方法。我把它当作一个字典来使用,同时使用列表的方法。如果调用了列表的方法,那么它会假设存储的项目是有顺序的,具体来说,就是字符串的排序优先于整数,并且键总是处于“排序”状态。
它还支持添加功能,作为这些方法的示例。这是基于我自己的使用场景。我需要从一个自动生成的字典中添加项目,但如果它不存在,就会创建并返回一个新的AutoVivification
对象。它们没有整数“值”,所以你不能这样做:
rp = AutoVivification()
rp['a']['b'] = 3
rp['a']['b'] + rp['q']
这样做就失去了意义,因为我不知道某个东西是否会存在,但我还是想要一个默认值。因此,我给它添加了__add__
和__radd__
方法。它们使用底层字典的length
作为integer
值,所以新创建的AV对象在加法运算时的值为零。如果某个键里有其他东西而不是AV对象,那么我们就会调用那个东西的加法方法(如果有实现的话)。
也许这个方法可以满足你在字典中使用任意数量的“维度”的需求:
a= collections.defaultdict(list)
你代码中唯一需要改动的地方是:
a['x', 'y'].append('z')
当然,这个解决方案是否适合你,取决于两个条件:
- 你是否需要方便地访问所有包含例如 'x' 的“第一维度”的列表
- 你是否更喜欢 Perl 的那种神奇的方式 :)
如果这两个条件中有一个成立,我的解决方案就可能对你没有帮助。
在编程中,有时候我们需要处理一些数据,这些数据可能来自不同的地方,比如用户输入、文件或者网络请求。为了让程序能够理解这些数据,我们通常需要将它们转换成程序能够处理的格式。
比如说,如果我们从一个网页上获取了一些信息,这些信息可能是以文本的形式存在的。为了让程序能够使用这些信息,我们需要将它们转换成适合的格式,比如数字、列表或者字典等。这样,程序才能对这些数据进行计算、排序或者其他操作。
在这个过程中,我们可能会用到一些工具和库,这些工具可以帮助我们更方便地处理数据。比如,有些库可以自动将文本转换成我们需要的格式,省去了手动转换的麻烦。
总之,数据处理是编程中非常重要的一部分,掌握如何将数据转换成程序能理解的格式,会让我们的编程工作变得更加顺利。
a = collections.defaultdict(lambda: collections.defaultdict(list))