Python 2.6.5 defaultdict覆盖__setitem__无限递归(Python 2.6.5 defaultdict override __setitem__ infinite recursion)

我想实现一个字典,可以对插入的键进行某些检查,例如请看下面:

from collections import defaultdict class CheckingDict(defaultdict): def __init__(self, *args, **kwargs): super(CheckingDict, self).__init__(*args, **kwargs) def __setitem__(self, key, value): if not super(CheckingDict, self).__missing__(key): raise ValueError("Key {key} is already present".format(key=key)) else: return defaultdict.__setitem__(self, key, value) a = CheckingDict(lambda: None) a[1] = 1

上面代码的问题是给我无限递归。 所以问题是为什么以及如何正确地做到这一点?

我不想使用合成,因为要获得defaultdict所有功能,我需要编写更多的代码。

I would like to implement a dictionary that would do certain checks on keys that get inserted, for example please see below:

from collections import defaultdict class CheckingDict(defaultdict): def __init__(self, *args, **kwargs): super(CheckingDict, self).__init__(*args, **kwargs) def __setitem__(self, key, value): if not super(CheckingDict, self).__missing__(key): raise ValueError("Key {key} is already present".format(key=key)) else: return defaultdict.__setitem__(self, key, value) a = CheckingDict(lambda: None) a[1] = 1

The problem with the code above is that is gives me infinite recursion. So the question is why and how to do it correctly?

I don't want to use composition since to get all the functionality of a defaultdict I'd need to write significantly more code.

最满意答案

这是导致问题的__missing__ ,并注意到:

如果它只调用超类,那么定义__init__是没有意义的; 和 实际设置项目时,您没有使用super 。

一个有效的实施:

class CheckingDict(defaultdict): def __setitem__(self, key, value): if key in self: raise ValueError("Key {!r} is already present".format(key)) super(CheckingDict, self).__setitem__(key, value)

那么为什么调用__missing__调用__setitem__ ,导致递归呢? 该方法不只是告诉您key是否丢失 ; 根据文件 (强调我的):

如果default_factory不是None ,则在不带参数的情况下调用[ __missing__ ]以提供给定键的默认值, 此值将插入到键的字典中 ,并返回。

__missing__实际上将默认值放入字典中,如果该键尚未存在,这意味着它必须调用__setitem__来执行此操作。

It's __missing__ that's causing the problem, and note that:

There's no point defining __init__ if it only calls the superclass; and You aren't using super when you actually set the item.

A working implementation:

class CheckingDict(defaultdict): def __setitem__(self, key, value): if key in self: raise ValueError("Key {!r} is already present".format(key)) super(CheckingDict, self).__setitem__(key, value)

So why does calling __missing__ call __setitem__, leading to the recursion? That method doesn't just tell you if the key is missing; per the documentation (emphasis mine):

If default_factory is not None, [__missing__] is called without arguments to provide a default value for the given key, this value is inserted in the dictionary for the key, and returned.

It is __missing__ that's actually putting the default value into the dictionary if the key isn't already there, which means that it must call __setitem__ to do so.

更多推荐