From: Ethan Furman on
John Posner wrote:
> On 8/3/2010 12:54 PM, Ethan Furman wrote:
>
> <snip>
>
>> I think mentioning how __missing__ plays into all this would be helpful.
>> Perhaps in the first paragraph, after the colon:
>>
>> if a key does not currently exist in a defaultdict object, __missing__
>> will be called with that key, which in turn will call a "default value
>> factory" to provide a value for that key.
>
> Thanks, Ethan. As I said (or at least implied) to Christian earlier in
> this thread, I don't want to repeat the mistake of the current
> description: confusing the functionality provided *by* the defaultdict
> class with underlying functionality (the dict type's __missing__
> protocol) that is used in the definition of the class.

I just went and read the entry that had the bogus claim -- personally, I
didn't see any confusion. I would like to point out the __missing__ is
*not* part of dicts (tested on 2.5 and 2.6 -- don't have 2.7 installed yet).

Having said that, I think your final paragraph is better than my first
paragraph edit.

> So I'd rather not mention __missing__ in the first paragraph, which
> describes the functionality provided *by* the defaultdict class. How
> about adding this para at the end:
>
> defaultdict is defined using functionality that is available to *any*
> subclass of dict: a missing-key lookup automatically causes the
> subclass's __missing__ method to be called, with the non-existent key
> as its argument. The method's return value becomes the result of the
> lookup.
>
> BTW, I couldn't *find* the coding of defaultdict in the Python 2.6
> library. File collections.py contains this code:
>
> from _abcoll import *
> import _abcoll
> __all__ += _abcoll.__all__
>
> from _collections import deque, defaultdict
>
> ... but I ran into a dead end after that. :-( I believe that the
> following *could be* the definition of defaultdict:
>
> class defaultdict(dict):
> def __init__(self, factory, *args, **kwargs):
> dict.__init__(self, *args, **kwargs)
> self.default_factory = factory
>
> def __missing__(self, key):
> """provide value for missing key"""
> value = self.default_factory() # call factory with no args
> self[key] = value
> return value

I think it's more along these lines:

class defaultdict(dict):
def __init__(self, factory=None, *args, **kwargs):
dict.__init__(self, *args, **kwargs)
self.default_factory = factory

def __missing__(self, key):
"provide value for missing key"
if self.default_factory is None:
raise KeyError("blah blah blah")
value = self.default_factory()
self[key] = value
return value

~Ethan~
From: Ethan Furman on
John Posner wrote:
> On 7/31/2010 1:31 PM, John Posner wrote:
>>
>> Caveat -- there's another description of defaultdict here:
>>
>> http://docs.python.org/library/collections.html#collections.defaultdict
>>
>> ... and it's bogus. This other description claims that __missing__ is a
>> method of defaultdict, not of dict.

__missing__ isn't a method of dict:

--> print dir(dict())
['__class__', '__cmp__', '__contains__', '__delattr__', '__delitem__',
'__doc__', '__eq__', '__ge__', '__getattribute__', '__getitem__',
'__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__',
'__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__',
'__repr__', '__setattr__', '__setitem__', '__str__', 'clear', 'copy',
'fromkeys', 'get', 'has_key', 'items', 'iteritems', 'iterkeys',
'itervalues', 'keys', 'pop', 'popitem', 'setdefault', 'update',
'values']

I will agree that the current defaultdict description does not make it
clear that __missing__ can be defined for *any* subclass of dict,
although the dict description does go over this... is that the confusion
you are talking about? If not, could you explain?

~Ethan~
From: Christian Heimes on
> I just went and read the entry that had the bogus claim -- personally, I
> didn't see any confusion. I would like to point out the __missing__ is
> *not* part of dicts (tested on 2.5 and 2.6 -- don't have 2.7 installed yet).

I beg your pardon but you are wrong. __missing__ is available for all
*subclasses* of dict since Python 2.5. See
http://svn.python.org/view/python/branches/release25-maint/Objects/dictobject.c?revision=81031&view=markup

>>> class mydict(dict):
.... def __missing__(self, key):
.... print "__missing__", key
.... raise KeyError(key)
....
>>> m = mydict()
>>> m[1]
__missing__ 1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in __missing__
KeyError: 1

Christian

From: Ethan Furman on
Christian Heimes wrote:
>> I just went and read the entry that had the bogus claim -- personally, I
>> didn't see any confusion. I would like to point out the __missing__ is
>> *not* part of dicts (tested on 2.5 and 2.6 -- don't have 2.7 installed yet).
>
> I beg your pardon but you are wrong. __missing__ is available for all
> *subclasses* of dict since Python 2.5. See
> http://svn.python.org/view/python/branches/release25-maint/Objects/dictobject.c?revision=81031&view=markup
>
>>>> class mydict(dict):
> ... def __missing__(self, key):
> ... print "__missing__", key
> ... raise KeyError(key)
> ...
>>>> m = mydict()
>>>> m[1]
> __missing__ 1
> Traceback (most recent call last):
> File "<stdin>", line 1, in <module>
> File "<stdin>", line 4, in __missing__
> KeyError: 1

Perhaps punctuation will help clarify my intent:

__missing__ is *not* part of (dict)s, as shown by dir(dict()):

['__class__', '__cmp__', '__contains__', '__delattr__', '__delitem__',
'__doc__', '__eq__', '__ge__', '__getattribute__', '__getitem__',
'__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__',
'__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__',
'__repr__', '__setattr__', '__setitem__', '__str__', 'clear', 'copy',
'fromkeys', 'get', 'has_key', 'items', 'iteritems', 'iterkeys',
'itervalues', 'keys', 'pop', 'popitem', 'setdefault', 'update',
'values']

And, just to state what is hopefully obvious, if you don't create
__missing__ yourself, it still isn't in the subclass:

--> class somedict(dict):
.... "Is __missing__ defined if I don't define it? Nope."
....
--> sd = somedict()
--> sd[1]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 1
--> dir(sd)
['__class__', '__cmp__', '__contains__', '__delattr__', '__delitem__',
'__dict__', '__doc__', '__eq__', '__ge__', '__getattribute__',
'__getitem__', '__gt__', '__hash__', '__init__', '__iter__', '__le__',
'__len__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__str__',
'__weakref__', 'clear', 'copy', 'fromkeys', 'get', 'has_key', 'items',
'iteritems', 'iterkeys', 'itervalues', 'keys', 'pop', 'popitem',
'setdefault', 'update', 'values']

~Ethan~
From: Christian Heimes on
> Perhaps punctuation will help clarify my intent:
>
> __missing__ is *not* part of (dict)s, as shown by dir(dict()):

Indeed, that's correct. Can we agree, that __missing__ is an optional
feature of the dict interface, that can be implemented in subclasses of
dict?

Christian