在搜索使用嵌套字典的方法时,我发现了nosklo发布的以下代码,请解释一下。
class AutoVivification(dict):
"""Implementation of perl's autovivification feature."""
def __getitem__(self, item):
try:
return dict.__getitem__(self, item)
except KeyError:
value = self[item] = type(self)()
return value测试:
a = AutoVivification()
a[1][2][3] = 4
a[1][3][3] = 5
a[1][2]['test'] = 6
print a输出:
{1: {2: {'test': 6, 3: 4}, 3: {3: 5}}}我是一个相当新手的程序员。我的大部分知识都是在业余时间学到的,我唯一的正式训练是高中时的Turbo Pascal。我理解并能够以简单的方式使用类,例如使用__init__、类方法,以及使用foo.man = 'choo'在类的实例中存储数据。
我的印象是类声明中的(dict)将由__init__处理。
不过,我以前也用过try: except:,用的方式也很简单。在我看来,当try运行时,它调用了一系列函数__getitem__。我估计,如果当前级别的字典存在,try将传递并转到下一个字典。我猜,except是在有KeyError的时候运行的,但我从来没有见过self像这样使用过。Self被当作字典对待,而我认为self是class AutoVivification的实例……两者都是吗?我从来没有像这样连续赋值两次foo = man = choo,但怀疑value指向self[item],而self[item]指向type(self)的结果。但是type(self)会返回这样的东西:<代码>d24,不是吗?我不知道末尾额外的圆括号是用来做什么的。因为我不知道函数是如何调用的,所以我不知道value返回到哪里。
对于所有的问题,我很抱歉!这里面有太多我不明白的地方,我不知道去哪里查找,除非我花了几个小时阅读文档,在这些文档中我几乎没有留下什么印象。这段代码看起来可以满足我的需求,但我想在使用它之前先理解它。
如果您想知道我试图在我的程序中使用嵌套字典做什么:我正在尝试以天文数字的规模保存地图数据。虽然我不能创建嵌套4次的10^6项的字典/列表(这将是10^24项!),但空间大部分是空的,所以我可以完全省略空值,只在那里有东西时才赋值。难住我的是一种处理字典的有效方法。
发布于 2012-11-08 03:07:02
逐行:
class AutoVivification(dict):我们做了一个dict的子类,所以AutoVivification是一种dict,有一些局部的变化。
def __getitem__(self, item):每当有人试图通过[...]索引查找访问实例上的项时,就会调用__getitem()__ hook。因此,每当有人执行object[somekey]时,就会调用type(object).__getitem__(object, somekey)。
我们暂时跳过try,下一行是:
return dict.__getitem__(self, item)这将调用未绑定的方法__getitem__(),并将我们自己的实例与键一起传递给它。换句话说,我们调用父类dict定义的原始__getitem__。
现在,我们都知道如果字典中没有item键会发生什么,就会引发KeyError。这是try:,except KeyError组合的用武之地:
try:
return dict.__getitem__(self, item)
except KeyError:
value = self[item] = type(self)()
return value因此,如果当前实例(它是dict的子类型)没有给定的键,它将捕获原始dict.__getitem__()方法抛出的KeyError异常,然后我们创建一个新值,将其存储在self[item]中并返回该值。
现在,记住self是dict的一个(子类),所以它是一个字典。因此,它可以分配新值(顺便使用__setitem__ hook ),在本例中,它创建了一个与self相同类型的新实例。这是另一个dict子类。
那么当我们调用a[1][2][3] = 4时会发生什么呢?Python会一步一步地完成这个过程:
a[1]通向type(a).__getitem__(a, 1)。AutoVivification的自定义__getitem__方法捕获KeyError,创建AutoVivification的新实例,将其存储在键1下,并返回it.a[1]返回一个空的AutoVivification实例。在该对象上调用下一个item access [2],我们重复步骤1中发生的事情;创建一个KeyError,创建AutoVivification的一个新实例,并将其存储在2键下,然后将该新实例返回给该对象,并返回一个空的AutoVivification实例。在该对象上调用下一个item access [3],我们重复步骤1(和步骤2)中发生的事情。存在一个KeyError,创建一个新的AutoVivification实例,并将其存储在3密钥下,然后将该新实例返回给返回一个空AutoVivification实例的3。现在,我们在该实例中存储一个新值4.一旦转到下一行代码a[1][3][3] = 5,顶级AutoVivification实例就已经有了一个1键,并且return dict.__getitem__(self, item)行将返回相应的值,它恰好是在上面的第一步中创建的AutoVivification实例。
在此基础上,[3] item access调用将再次创建一个新的AutoVivification实例(因为a[1]处的对象只有一个2键),我们将再次执行所有相同的步骤。
发布于 2012-11-08 03:05:32
首先,请参阅object.__getitem__文档。
class AutoVivification(dict)声明使AutoVivification成为dict的子类,因此它的行为与dict相同,除非它显式覆盖某些行为--就像该类覆盖__getitem__时所做的那样。
对dict.__getitem__(self, item)的调用通常会写成:
super(AutoVivification, self).__getitem__(item)(至少在Python2.x中是这样;Python3有更好的语法。)无论采用哪种方式,这都会尝试让默认的dict行为运行,但在不起作用的情况下实现一个后备。
type(self)()首先查找与self实例对应的类对象,然后调用类对象--在本例中,这与编写AutoVivification()相同,看起来要熟悉得多。
希望这能让你明白这一点!
https://stackoverflow.com/questions/13276218
复制相似问题