在python中,以下AutoVivification类如何工作?

Kho*_*ono 13 python dictionary autovivification

在寻找使用嵌套字典的方法时,我发现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
Run Code Online (Sandbox Code Playgroud)

测试:

a = AutoVivification()

a[1][2][3] = 4
a[1][3][3] = 5
a[1][2]['test'] = 6

print a
Run Code Online (Sandbox Code Playgroud)

输出:

{1: {2: {'test': 6, 3: 4}, 3: {3: 5}}}
Run Code Online (Sandbox Code Playgroud)

我是一个非常新手的程序员.我在自己的时间里学到了大部分我所知道的知识,我在高中时只接受过Turbo Pascal的正式训练.我理解并能够以简单的方式使用类,例如使用__init__类方法,并在类的实例中存储数据foo.man = 'choo'.

我不知道方括号系列如何正确地通过类(我假设它们__getitem__以某种方式调用)并且不明白它们如何被如此简洁地处理而不必单独调用该方法三次.

我的印象是(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)会返回这样的东西:<class '__main__.AutoVivification'>不是吗?我不知道最后有什么额外的圆括号.因为我不知道如何调用函数,我不明白value返回的位置.

抱歉有这些问题!有这么多,我不明白,我不知道在哪里查阅,只需阅读文档几个小时,我保留很少.这段代码看起来像是为了我的目的,但我想在使用之前理解它.

如果你想知道我在我的程序中尝试使用嵌套字典做什么:我试图以天文数字的形式保存地图数据.虽然我无法创建嵌套4次的10 ^ 6项的字典/列表(也就是10 ^ 24项!),但空间大部分都是空的,所以我可以完全保留空值,只有在那里有东西时才分配.困扰我的是处理字典的有效方式.

Mar*_*ers 19

逐行:

class AutoVivification(dict):
Run Code Online (Sandbox Code Playgroud)

我们创建了一个子类dict,因此AutoVivification是一种dict局部变化.

def __getitem__(self, item):
Run Code Online (Sandbox Code Playgroud)

每当有人试图通过索引查找访问实例上的项时,就会调用该__getitem()__钩子[...].所以每当有人这样做object[somekey],都会type(object).__getitem__(object, somekey)被召集.

我们try稍等一下,下一行是:

 return dict.__getitem__(self, item)
Run Code Online (Sandbox Code Playgroud)

这将调用未绑定的方法__getitem__(),并将自己的实例与密钥一起传递给它.换句话说,我们所说的原来 __getitem__我们的父类定义dict.

现在,我们都知道如果item字典中没有键,会发生什么KeyError.这就是try:,except KeyError组合进来:

    try:
        return dict.__getitem__(self, item)
    except KeyError:
        value = self[item] = type(self)()
        return value
Run Code Online (Sandbox Code Playgroud)

因此,如果当前实例(它是子类型dict)没有给定键,它将捕获KeyError原始dict.__getitem__()方法抛出的异常,而是创建一个值,将其存储self[item]并返回该值.

现在,请记住它self是(子类)dict,所以它是一个字典.因此,它可以分配新的值(它将使用__setitem__钩子,在其中使用钩子),在这种情况下,它创建一个与之相同类型的实例self.那是另一个dict子类.

那么当我们打电话时会发生什么a[1][2][3] = 4呢?Python经历了这一步:

  1. a[1]导致type(a).__getitem__(a, 1).捕获的自定义__getitem__方法,创建一个实例,存储在键下并返回它.AutoVivificationKeyErrorAutoVivification1

  2. a[1]返回一个空AutoVivification实例.[2]在该对象上调用下一个项目访问,我们重复步骤1中发生的事情; 有一个KeyError,AutoVivification创建一个新实例,存储在2密钥下,并将新实例返回给调用者.

  3. a[1][2]返回一个空AutoVivification实例.[3]在该对象上调用下一个项访问,我们重复步骤1(以及步骤2)中发生的事情.还有一个KeyError,的新实例AutoVivification被创建,下存储3密钥,即新的实例返回给调用者.

  4. a[1][2][3]返回一个空AutoVivification实例.现在我们在该实例中存储一个新值4.

进入下一行代码后a[1][3][3] = 5,顶级AutoVivification实例已经有一个1键,该return dict.__getitem__(self, item)行将返回相应的值,该值恰好是AutoVivification上面步骤1中创建的实例.

从那里,[3]项目访问调用将AutoVivification再次创建一个新实例(因为该对象a[1]只有一个2键),我们再次执行所有相同的步骤.