[Python-Dev] Creating dicts from dict subclasses

Walter Dörwald walter at livinglogic.de
Thu Dec 14 14:11:38 CET 2006


Guido van Rossum wrote:
> On 12/13/06, Walter Dörwald <walter at livinglogic.de> wrote:
>> Guido van Rossum wrote:
>> > On 12/13/06, Walter Dörwald <walter at livinglogic.de> wrote:
>> >> I tried to reimplement weakref.WeakValueDictionary as a subclass of
>> >> dict. The test passes except for one problem: To compare results
>> >> test_weakref.py converts a weakdict to a real dict via dict(weakdict).
>> >> This no longer works because PyDict_Merge() does a PyDict_Check()
>> on the
>> >> argument and then ignores all overwritten methods. (The old version
>> >> worked because UserDict.UserDict was used).
>> >>
>> >> The simplest solution is to replace the PyDict_Check() call with
>> >> PyDict_CheckExact(), but this might slow things down too much, because
>> >> the fallback code basically does:
>> >>
>> >>    for key in iter(arg.keys()):
>> >>       self[key] = arg.__getitem__(key)
>> >>
>> >> Why can't we use:
>> >>
>> >>    for key in iter(arg):
>> >>       self[key] = arg.__getitem__(key)
>> >>
>> >> instead?
>> >
>> > The only reason I can think of is backwards compatibility: not all
>> > "mappings" created pre-2.2 would support iteration. Maybe you could
>> > check for a tp_iter slot and if non-NULL use the latter otherwise use
>> > the original fallback?
>>
>> This doesn't seem to work. It breaks test_update() in test_dict.py which
>> does this:
>>
>>     d = {}
>>     class SimpleUserDict:
>>         def __init__(self):
>>             self.d = {1:1, 2:2, 3:3}
>>         def keys(self):
>>             return self.d.keys()
>>         def __getitem__(self, i):
>>             return self.d[i]
>>     d.update(SimpleUserDict())
>>     self.assertEqual(d, {1:1, 2:2, 3:3})
>>
>> This fails with
>>
>>     KeyError: 0
>>
>> because SimpleUserDict doesn't implement __iter__, so it gets an
>> iterator implementation via __getitem__.
>>
>> So maybe this only makes sense for Python 3.0 where we can demand that
>> dict-like classes implement __iter__?
> 
> Ah, right. But I think you should still use PyDict_CheckExact, and
> slow fallbacks be damned. (I guess you could look for iterkeys first.)

OK, here's a patch that tries iterkeys() before keys():
http://bugs.python.org/1615701

Both versions seem to be faster than Python 2.5:

class dictik:
    def __init__(self, n):
        self.d = dict((i, i) for i in xrange(n))
    def iterkeys(self):
        return iter(self.d)
    def __getitem__(self, i):
        return self.d[i]

class dictk:
    def __init__(self, n):
        self.d = dict((i, i) for i in xrange(n))
    def keys(self):
        return self.d.keys()
    def __getitem__(self, i):
        return self.d[i]

$ python2.5 -mtimeit -s'from foo import dictik, dictk; d=dictk(100000)'
'dict(d)'
10 loops, best of 3: 179 msec per loop
$ ./python -mtimeit -s'from foo import dictik, dictk; d=dictk(100000)'
'dict(d)'
10 loops, best of 3: 138 msec per loop
$ ./python -mtimeit -s'from foo import dictik, dictk; d=dictik(100000)'
'dict(d)'
10 loops, best of 3: 123 msec per loop

Servus,
   Walter


More information about the Python-Dev mailing list