首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在python中,下面的AutoVivification类是如何工作的?

在python中,下面的AutoVivification类是如何工作的?
EN

Stack Overflow用户
提问于 2012-11-08 02:52:10
回答 2查看 1.4K关注 0票数 13

在搜索使用嵌套字典的方法时,我发现了nosklo发布的以下代码,请解释一下。

代码语言:javascript
复制
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

测试:

代码语言:javascript
复制
a = AutoVivification()

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

print a

输出:

代码语言:javascript
复制
{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被当作字典对待,而我认为selfclass AutoVivification的实例……两者都是吗?我从来没有像这样连续赋值两次foo = man = choo,但怀疑value指向self[item],而self[item]指向type(self)的结果。但是type(self)会返回这样的东西:<代码>d24,不是吗?我不知道末尾额外的圆括号是用来做什么的。因为我不知道函数是如何调用的,所以我不知道value返回到哪里。

对于所有的问题,我很抱歉!这里面有太多我不明白的地方,我不知道去哪里查找,除非我花了几个小时阅读文档,在这些文档中我几乎没有留下什么印象。这段代码看起来可以满足我的需求,但我想在使用它之前先理解它。

如果您想知道我试图在我的程序中使用嵌套字典做什么:我正在尝试以天文数字的规模保存地图数据。虽然我不能创建嵌套4次的10^6项的字典/列表(这将是10^24项!),但空间大部分是空的,所以我可以完全省略空值,只在那里有东西时才赋值。难住我的是一种处理字典的有效方法。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2012-11-08 03:07:02

逐行:

代码语言:javascript
复制
class AutoVivification(dict):

我们做了一个dict的子类,所以AutoVivification是一种dict,有一些局部的变化。

代码语言:javascript
复制
def __getitem__(self, item):

每当有人试图通过[...]索引查找访问实例上的项时,就会调用__getitem()__ hook。因此,每当有人执行object[somekey]时,就会调用type(object).__getitem__(object, somekey)

我们暂时跳过try,下一行是:

代码语言:javascript
复制
 return dict.__getitem__(self, item)

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

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

代码语言:javascript
复制
    try:
        return dict.__getitem__(self, item)
    except KeyError:
        value = self[item] = type(self)()
        return value

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

现在,记住selfdict的一个(子类),所以它是一个字典。因此,它可以分配新值(顺便使用__setitem__ hook ),在本例中,它创建了一个与self相同类型的新实例。这是另一个dict子类。

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

  1. a[1]通向type(a).__getitem__(a, 1)AutoVivification的自定义__getitem__方法捕获KeyError,创建AutoVivification的新实例,将其存储在键1下,并返回it.
  2. 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键),我们将再次执行所有相同的步骤。

票数 19
EN

Stack Overflow用户

发布于 2012-11-08 03:05:32

首先,请参阅object.__getitem__文档。

class AutoVivification(dict)声明使AutoVivification成为dict的子类,因此它的行为与dict相同,除非它显式覆盖某些行为--就像该类覆盖__getitem__时所做的那样。

dict.__getitem__(self, item)的调用通常会写成:

代码语言:javascript
复制
super(AutoVivification, self).__getitem__(item)

(至少在Python2.x中是这样;Python3有更好的语法。)无论采用哪种方式,这都会尝试让默认的dict行为运行,但在不起作用的情况下实现一个后备。

type(self)()首先查找与self实例对应的类对象,然后调用类对象--在本例中,这与编写AutoVivification()相同,看起来要熟悉得多。

希望这能让你明白这一点!

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/13276218

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档