Ian*_*and 42 python dictionary subclass
我正在构建一个子类dict和覆盖的类__setitem__.我想确定我的方法将在可能设置字典项的所有实例中被调用.
我发现了三种情况,Python(在这种情况下,2.6.4)__setitem__在设置值时不会调用我的重写方法,而是PyDict_SetItem直接调用
setdefault方法中update方法中作为一个非常简单的测试:
class MyDict(dict):
def __setitem__(self, key, value):
print "Here"
super(MyDict, self).__setitem__(key, str(value).upper())
>>> a = MyDict(abc=123)
>>> a['def'] = 234
Here
>>> a.update({'ghi': 345})
>>> a.setdefault('jkl', 456)
456
>>> print a
{'jkl': 456, 'abc': 123, 'ghi': 345, 'def': '234'}
Run Code Online (Sandbox Code Playgroud)
您可以看到仅在显式设置项时才调用重写的方法.为了让Python始终调用我的__setitem__方法,我不得不重新实现这三种方法,如下所示:
class MyUpdateDict(dict):
def __init__(self, *args, **kwargs):
self.update(*args, **kwargs)
def __setitem__(self, key, value):
print "Here"
super(MyUpdateDict, self).__setitem__(key, value)
def update(self, *args, **kwargs):
if args:
if len(args) > 1:
raise TypeError("update expected at most 1 arguments, got %d" % len(args))
other = dict(args[0])
for key in other:
self[key] = other[key]
for key in kwargs:
self[key] = kwargs[key]
def setdefault(self, key, value=None):
if key not in self:
self[key] = value
return self[key]
Run Code Online (Sandbox Code Playgroud)
有没有其他方法需要覆盖,以便知道Python将始终调用我的__setitem__方法?
UPDATE
根据gs的建议,我已经尝试了子类化UserDict(实际上,IterableUserDict,因为我想迭代键),如下所示:
from UserDict import *;
class MyUserDict(IterableUserDict):
def __init__(self, *args, **kwargs):
UserDict.__init__(self,*args,**kwargs)
def __setitem__(self, key, value):
print "Here"
UserDict.__setitem__(self,key, value)
Run Code Online (Sandbox Code Playgroud)
这个类似乎正确地调用了我__setitem__的setdefault,但它没有调用它update,或者初始数据被提供给构造函数.
更新2
Peter Hansen的建议让我更仔细地看看dictobject.c,并且我意识到更新方法可以简化一些,因为内置的字典构造函数无论如何都只是调用内置的更新方法.它现在看起来像这样:
def update(self, *args, **kwargs):
if len(args) > 1:
raise TypeError("update expected at most 1 arguments, got %d" % len(args))
other = dict(*args, **kwargs)
for key in other:
self[key] = other[key]
Run Code Online (Sandbox Code Playgroud)
Ian*_*and 48
我回答我的问题,因为我最终决定,我真的不希望继承快译通,而不是创建一个新的映射类,和UserDict中仍推迟到底层快译通对象在某些情况下,而不是使用所提供的__setitem__.
阅读和重新阅读了Python 2.6.4源之后(主要是Objects/dictobject.c,但我grepped eveywhere别的,看看那里的各种方法的使用,)我的理解是,下面的代码是足够了我的__setitem__称为每次对象是改变了,否则就像Python Dict一样:
Peter Hansen的建议让我更仔细地看了一下dictobject.c,我意识到我的原始答案中的更新方法可以简化一些,因为内置的字典构造函数无论如何都只是调用内置的更新方法.所以我的答案中的第二次更新已被添加到下面的代码中(由一些有用的人;-).
class MyUpdateDict(dict):
def __init__(self, *args, **kwargs):
self.update(*args, **kwargs)
def __setitem__(self, key, value):
# optional processing here
super(MyUpdateDict, self).__setitem__(key, value)
def update(self, *args, **kwargs):
if args:
if len(args) > 1:
raise TypeError("update expected at most 1 arguments, "
"got %d" % len(args))
other = dict(args[0])
for key in other:
self[key] = other[key]
for key in kwargs:
self[key] = kwargs[key]
def setdefault(self, key, value=None):
if key not in self:
self[key] = value
return self[key]
Run Code Online (Sandbox Code Playgroud)
我用这段代码测试了它:
def test_updates(dictish):
dictish['abc'] = 123
dictish.update({'def': 234})
dictish.update(red=1, blue=2)
dictish.update([('orange', 3), ('green',4)])
dictish.update({'hello': 'kitty'}, black='white')
dictish.update({'yellow': 5}, yellow=6)
dictish.setdefault('brown',7)
dictish.setdefault('pink')
try:
dictish.update({'gold': 8}, [('purple', 9)], silver=10)
except TypeError:
pass
else:
raise RunTimeException("Error did not occur as planned")
python_dict = dict([('b',2),('c',3)],a=1)
test_updates(python_dict)
my_dict = MyUpdateDict([('b',2),('c',3)],a=1)
test_updates(my_dict)
Run Code Online (Sandbox Code Playgroud)
它通过了.我尝试过的所有其他实现在某些方面都失败了.我仍然接受任何答案,告诉我我错过了什么,但除此之外,我在几天之内勾选了这个旁边的复选标记,并称之为正确答案:)
| 归档时间: |
|
| 查看次数: |
31181 次 |
| 最近记录: |