在寻找使用嵌套字典的方法时,我发现了 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
当我想的时候,它被当作一本字典对待 self
是 class 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]
中并返回该值。
现在,请记住 self
是 dict
的(子类),因此它是一个字典。因此,它可以分配新值(顺便说一下,它会使用 __setitem__
hook),并且在这种情况下,它会创建一个与 self
类型相同的 new 实例.那是另一个 dict
子类。
那么当我们调用 a[1][2][3] = 4
时具体会发生什么? Python 一步一步地完成这个过程:
a[1]
导致type(a).__getitem__(a, 1)
。AutoVivification
的自定义__getitem__
方法捕获KeyError
,创建 新AutoVivification
实例>,将其存储在键1
下并返回它。a[1]
返回了一个空的AutoVivification
实例。对该对象调用下一个项目访问[2]
,我们重复步骤 1 中发生的事情;有一个KeyError
,一个新的AutoVivification
实例被创建,存储在2
键下,并且这个新实例被返回给调用者。a[1][2]
返回了一个空的AutoVivification
实例。对该对象调用下一个项目访问[3]
,我们重复步骤 1(和步骤 2)中发生的事情。有一个KeyError
,一个新的AutoVivification
实例被创建,存储在3
键下,然后那个新实例被返回给调用者。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/