我正在对OrderedDict(Cpython,2.7.3)进行子类化以表示数据文件。 __getitem__
从数据文件中提取一个字段并将其设置在当前实例上,类似于我在下面发布的代码。现在我想覆盖__contains__
以返回True
如果字段在字典中或磁盘上的文件中,因为它可以以任何方式读取。但是,这似乎打破了OrderedDict
检查密钥的能力。
from collections import OrderedDict
dictclass = OrderedDict
class Foo(dictclass):
def __getitem__(self,key):
try:
return dictclass.__getitem__(self,key)
except KeyError:
pass
data = key*2
self[key] = data
return data
def __contains__(self,whatever):
return dictclass.__contains__(self,whatever) or 'bar' in whatever
a = Foo()
print a['bar']
print a.keys()
如果您运行上面的代码,您将获得此输出:
barbar
[]
请注意,如果您在上面的代码中更改dictclass = dict
,它似乎仍然有用(提供以下输出)。
barbar
['bar']
我做错了什么?
答案 0 :(得分:6)
未定义Foo.__contains__
时:
a['bar']
调用Foo.__getitem__
,执行
self[key] = data
这会调用OrderedDict.__setitem__
,这是以这种方式定义的:
def __setitem__(self, key, value, PREV=0, NEXT=1, dict_setitem=dict.__setitem__):
'od.__setitem__(i, y) <==> od[i]=y'
# Setting a new item creates a new link at the end of the linked list,
# and the inherited dictionary is updated with the new key/value pair.
if key not in self:
root = self.__root
last = root[PREV]
last[NEXT] = root[PREV] = self.__map[key] = [last, root, key]
dict_setitem(self, key, value)
由于未定义Foo.__contains__
,
if key not in self:
是真的。因此,密钥已正确添加到self.__root
和self.__map
。
定义Foo.__contains__
时,
if key not in self:
如果错误。因此,密钥未正确添加到self.__root
和self.__map
。
Foo.__contains__
有效的傻瓜OrderedDict.__setitem__
认为已经添加了'bar'
密钥。
我发现使用以下代码(在__setitem__
和__iter__
中添加打印语句)很有帮助:
from collections import OrderedDict
dictclass = OrderedDict
class Foo(dictclass):
def __getitem__(self,key):
try:
return dictclass.__getitem__(self,key)
except KeyError:
pass
data = key*2
self[key] = data
return data
def __contains__(self,whatever):
print('contains: {}'.format(whatever))
return dictclass.__contains__(self,whatever) or 'bar' in whatever
def __setitem__(self, key, value, PREV=0, NEXT=1, dict_setitem=dict.__setitem__):
'od.__setitem__(i, y) <==> od[i]=y'
# Setting a new item creates a new link at the end of the linked list,
# and the inherited dictionary is updated with the new key/value pair.
print('key not in self: {}'.format(key not in self))
if key not in self:
root = self._OrderedDict__root
last = root[PREV]
last[NEXT] = root[PREV] = self._OrderedDict__map[key] = [last, root, key]
dict_setitem(self, key, value)
def __iter__(self):
'od.__iter__() <==> iter(od)'
# Traverse the linked list in order.
NEXT, KEY = 1, 2
root = self._OrderedDict__root
curr = root[NEXT]
print('curr: {}'.format(curr))
print('root: {}'.format(root))
print('curr is not root: {}'.format(curr is not root))
while curr is not root:
yield curr[KEY]
curr = curr[NEXT]
a = Foo()
print a['bar']
# barbar
print a.keys()
# ['bar']
请注意,您可以通过将Foo
作为collections.MutableMapping
的子类并将其大多数行为委托给OrderedDict
属性来避免此问题:
import collections
dictclass = collections.OrderedDict
class Foo(collections.MutableMapping):
def __init__(self, *args, **kwargs):
self._data = dictclass(*args, **kwargs)
def __setitem__(self, key, value):
self._data[key] = value
def __delitem__(self, key):
del self._data[key]
def __iter__(self):
return iter(self._data)
def __len__(self):
return len(self._data)
def __getitem__(self,key):
try:
return self._data[key]
except KeyError:
pass
data = key*2
self[key] = data
return data
def __contains__(self,whatever):
return dictclass.__contains__(self,whatever) or 'bar' in whatever
产生
a = Foo()
print a['bar']
# barbar
print a.keys()
# ['bar']
即使定义了__contains__
。
答案 1 :(得分:2)
or 'bar' in whatever
会破坏您的代码。如果您将其删除,它将与您提及的更改dictclass = dict
一样工作。
__setitem__
的{{1}}实现是:
OrderedDict
因此,对于def __setitem__(self, key, value, dict_setitem=dict.__setitem__):
'od.__setitem__(i, y) <==> od[i]=y'
# Setting a new item creates a new link at the end of the linked list,
# and the inherited dictionary is updated with the new key/value pair.
if key not in self:
root = self.__root
last = root[0]
last[1] = root[0] = self.__map[key] = [last, root, key]
return dict_setitem(self, key, value)
,条件应该为False,但即使在插入任何项目之前它也是True。因此,密钥未添加到self["bar"] = "barbar"
中使用的self.__root
:
OrderedDict.__iter__
由于检索值的代码使用此迭代器而def __iter__(self):
'od.__iter__() <==> iter(od)'
# Traverse the linked list in order.
root = self.__root
curr = root[1] # start at the first node
while curr is not root:
yield curr[2] # yield the curr[KEY]
curr = curr[1] # move to next node
不包含self.__root
,因此无法在值中返回此具体键。