Question about collections.defaultdict

Steven W. Orr steveo at syslang.net
Wed Mar 28 17:03:28 CEST 2012


On 3/26/2012 11:52 AM, Robert Kern wrote:
> On 3/26/12 4:33 PM, Steven W. Orr wrote:
>> On 3/26/2012 9:44 AM, Robert Kern wrote:
>>> On 3/26/12 2:33 PM, Steven W. Orr wrote:
>>>> I created a new class called CaseInsensitiveDict (by stealing from code I
>>>> found
>>>> on the web, thank you very much). The new class inherits from dict. It
>>>> makes it
>>>> so that if the key has a 'lower' method, it will always access the key using
>>>> lower
>>>>
>>>> I'd like to change the place where I previously declared a dict
>>>>
>>>> self.lookup = defaultdict(list)
>>>>
>>>> so that the new code will allow this new dict to be used instead. But then I
>>>> realized I may have painted myself into a small corner:
>>>>
>>>> Is there a way to use defaultdict so that I can override what *kind* of
>>>> dict it
>>>> will use?
>>>
>>> No.
>>>
>>>> I would like the value to still be a list be default, but it seems like I
>>>> can't
>>>> tell defaultdict to use *my* new dict.
>>>>
>>>> Do I give up on defaultdict?
>>>
>>> Assuming that your CaseInsensitiveDict subclasses from dict or UserDict, it's
>>> relatively easy to make a subclass of your CaseInsensitiveDict act like a
>>> defaultdict. Just implement the __missing__(key) method appropriately (and
>>> modify the constructor to take the callable, of course).
>>>
>>> http://docs.python.org/library/stdtypes.html#dict
>>> http://docs.python.org/library/collections.html#collections.defaultdict.__missing__
>>>
>>>
>>>
>>>
>>
>> I'm not quite getting what you're telling me, but I'm sure you have the right
>> idea. Here's the beginning of my class:
>>
>> class CaseInsensitiveDict(dict):
>> def __init__(self, init=None):
>> if isinstance(init, (dict, list, tuple)):
>> for kk, vv in init.items():
>> self[self.key_has_lower(kk)] = vv
>>
>>
>> It sounds like you want me to subclass defaultdict to create something like
>> this?
>>
>> class CaseInsensitiveDictDef(defaultdict):
>> def __init__(self, init=None):
>> super(CaseInsensitiveDictDef, self).__init__(list)
>> self.__missing__ = list
>>
>> I think I'm way off base. I'm not clear on what the calling sequence is for
>> defaultdict or how to get it to use my CaseInsensitiveDict instead of
>> regular dict.
>>
>> Can you help?
>
> You need to make a subclass of CaseInsensitiveDict, implement the
> __missing__(key) method, and override the __init__() method to take the
> factory function as an argument instead of data. defaultdict is just a
> subclass of dict that does this.
>
>
> class CaseInsensitiveDictDef(CaseInsensitiveDict):
> def __init__(self, default_factory):
> super(CaseInsensitiveDictDef, self).__init__()
> self.default_factory = default_factory
>
> def __missing__(self, key):
> return self.default_factory()
>

Many thanks. This was a great learning experience as well as ending up with 
exactly what I wanted.

Python is rich with "Ah ha!" moments. This was definitely one of them.

In my feeble attempt to give back, here's the answer:

class CaseInsensitiveDefaultDict(CaseInsensitiveDict):
     def __init__(self, default_factory=None, init=None):
         if not callable(default_factory):
             raise TypeError('First argument must be callable')
         super(CaseInsensitiveDefaultDict, self).__init__(init)
         self.default_factory = default_factory

     def __missing__(self, key):
         self[key] = val = self.default_factory()
         return val

     def __getitem__(self, key):
         try:
             return super(CaseInsensitiveDefaultDict, self).__getitem__(key)
         except KeyError:
             return self.__missing__(key)


-- 
Time flies like the wind. Fruit flies like a banana. Stranger things have  .0.
happened but none stranger than this. Does your driver's license say Organ ..0
Donor?Black holes are where God divided by zero. Listen to me! We are all- 000
individuals! What if this weren't a hypothetical question?
steveo at syslang.net



More information about the Python-list mailing list