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

标签 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

测试:

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' .

我不知道方括号系列是如何通过类正确地定向的(我假设他们正在调用 __getitem__ 以某种方式)并且不明白如何在不必调用方法的情况下如此简洁地处理它们单独三次。

我的印象是 (dict)在类声明中将由 __init__ 处理.

我用过 try: except:之前,虽然再次以非常简单的方式。在我看来它像 try ,当它运行时,正在调用一系列函数 __getitem__ .我收集到如果当前级别的字典存在,则尝试将通过并转到下一个字典。 except ,我收集,当有 KeyError 时运行但我没看到self像以前那样使用。 Self当我想的时候,它被当作一本字典对待 selfclass AutoVivification 的实例……两者都有吗?我从来没有像这样连续分配过两次foo = man = choo但怀疑value指向 self[item]同时self[item]指向 type(self) 的结果.但是type(self)会返回这样的东西:<class '__main__.AutoVivification'>不是吗?我不知道末尾的额外圆括号是做什么用的。因为不知道函数是怎么调用的,所以不明白在哪里value正在返回。

抱歉所有问题!这里面有太多我不明白的地方,我不知道去哪里找它,除非我花几个小时阅读文档,而在这些时间里,我只保留了很少的东西。这段代码看起来可以满足我的目的,但我想在使用它之前先了解它。

如果您想知道我在使用嵌套词典的程序中尝试做什么:我正在尝试将 map 数据保存在天文尺度上。虽然我无法创建嵌套 4 次的 10^6 项的字典/列表(那将是 10^24 项!),但空间大部分是空的,因此我可以完全保留空值,仅在有内容时才分配。困扰我的是处理字典的有效方法。

最佳答案

逐行:

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)

这会调用 unbound 方法 __getitem__(),并将我们自己的实例连同 key 一起传递给它。换句话说,我们调用父类 dict 定义的原始 __getitem__

现在,我们都知道如果字典中没有 item 键会发生什么,会引发 KeyError。这就是 try:, except KeyError 组合出现的地方:

    try:
        return dict.__getitem__(self, item)
    except KeyError:
        value = self[item] = type(self)()
        return value

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

现在,请记住 selfdict 的(子类),因此它是一个字典。因此,它可以分配新值(顺便说一下,它会使用 __setitem__ hook),并且在这种情况下,它会创建一个与 self 类型相同的 new 实例.那是另一个 dict 子类。

那么当我们调用 a[1][2][3] = 4 时具体会发生什么? Python 一步一步地完成这个过程:

  1. a[1] 导致 type(a).__getitem__(a, 1)AutoVivification 的自定义 __getitem__ 方法捕获 KeyError,创建 AutoVivification 实例>,将其存储在键 1 下并返回它。

  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实例上面的第一步。

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

关于python - 在 python 中,以下 AutoVivification 类是如何工作的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13276218/

相关文章:

python - 在 python 中散列字符串会返回错误的结果?

java - 使用 AppEngine 访问本地 map 数据

python - 从字典创建数据框 - python

python - 实现嵌套字典的最佳方法是什么?

php - PHP 有自动激活吗?

python - 如何获取beautifulsoup中所选标签的下一个标签(元素)

Python 线程 'While' 不正常

python - 如何扩展 django oscar 客户模型字段?

c++ - 格式化具有相似返回值的顺序 if 语句 (C++)

python - 我如何进行高级 Python 哈希自动生成?