classification
Title: Need example of using __missing__
Type: enhancement Stage: resolved
Components: Documentation Versions: Python 3.1, Python 3.2, Python 2.7
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: rhettinger Nosy List: alex, docs@python, eric.araujo, eric.smith, holdenweb, lukasz.langa, michael.foord, rhettinger
Priority: low Keywords:

Created on 2010-11-25 17:00 by lukasz.langa, last changed 2011-01-09 00:06 by eric.araujo. This issue is now closed.

Messages (16)
msg122382 - (view) Author: Łukasz Langa (lukasz.langa) * (Python committer) Date: 2010-11-25 17:00
Currently the constructor in defaultdict only accepts factories. It would be very handy to allow for concrete values as well. It's implementable either by checking if the argument is callable or by a new keyword argument.
msg122383 - (view) Author: Michael Foord (michael.foord) * (Python committer) Date: 2010-11-25 17:07
I would love this functionality (I almost always initialise defaultdict with a lambda that just returns a concrete value).

Unfortunately it seems like adding a keyword argument isn't possible because defaultdict takes arbitrary keyword args (and populates the dict with them).

    defaultdict(lambda: 1, foo=1, bar=2, baz=3)
msg122386 - (view) Author: Eric V. Smith (eric.smith) * (Python committer) Date: 2010-11-25 17:31
-1 from me. You can't use keywords, and if you make the value callable at a later date then suddenly you'll change the behavior of seemingly unrelated code. Is a lambda so bad?
msg122387 - (view) Author: Łukasz Langa (lukasz.langa) * (Python committer) Date: 2010-11-25 17:36
Both arguments are true and definitive. Last possibility would be to introduce a factory function for defaultdicts that would only accept concrete values:

  from collections import fallbackdict

Then this factory could produce defaultdict instances like this:

  fallbackdict(0, some_key=1) --> defaultdict(lambda: 0, some_key=1)

That would be safe for future type changes as well.

If this version does not sound appealing, I can't see any other way of reliably introducing this shortcut.
msg122388 - (view) Author: Eric V. Smith (eric.smith) * (Python committer) Date: 2010-11-25 17:48
How about:

from collections import defaultdict

class defaultdict_value(defaultdict):
    def __init__(self, value):
        defaultdict.__init__(self, lambda : value)

x = defaultdict_value(3)
print(x[1])
msg122389 - (view) Author: Steve Holden (holdenweb) * (Python committer) Date: 2010-11-25 17:54
On 11/25/2010 11:48 AM, Eric Smith wrote:
> 
> Eric Smith <eric@trueblade.com> added the comment:
> 
> How about:
> 
> from collections import defaultdict
> 
> class defaultdict_value(defaultdict):
>     def __init__(self, value):
>         defaultdict.__init__(self, lambda : value)
> 
> x = defaultdict_value(3)
> print(x[1])
> 
> ----------

+1

But I'd call it defaultdict_const().

regards
 Steve
-- 
Steve Holden           +1 571 484 6266   +1 800 494 3119
PyCon 2011 Atlanta March 9-17       http://us.pycon.org/
See Python Video!       http://python.mirocommunity.org/
Holden Web LLC                 http://www.holdenweb.com/
msg122391 - (view) Author: Éric Araujo (eric.araujo) * (Python committer) Date: 2010-11-25 17:55
Like three-liners?  whatsnew/2.5 gives us this one:

class zerodict(dict):
    def __missing__(self, key):
        return 0

I don’t think it’s too painful to have to use defaultdict with a lambda.  We can’t use a keyword argument and I’m -0.5 on changing behavior depending on the type of the first argument.
msg122396 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2010-11-25 19:09
> It would be very handy to allow for concrete values as well.

Do you have use cases for a concrete integer value that isn't zero?

Since we can currently use defaultdict(int) or defaultdict(tuple), is the purpose just to create a more direct spelling of the same thing?  The docs for defaultdict also show a general purpose way to generate any default constant (though that way isn't obvious if you haven't seen it in the docs).

I'm reluctant to add yet another variant.  We already have __missing__, defaultdict, Counter, dict.get, and dict.setdefault().

The docs for dictionaries need to make clear that Guido has already provided an idiom to be the one obvious way to do it:

    class MyConst(dict):
        def __missing__(self, key):
            return myconst

That was really the whole point of adding __missing__ in the first place.  The OP's proposal amounts to rejecting Guido's design which provides a very good general purpose solution.
msg122399 - (view) Author: Łukasz Langa (lukasz.langa) * (Python committer) Date: 2010-11-25 19:44
A couple of points:

1. Eric's proposal is what I had in mind with the `fallbackdict' idea.
2. I'm also reluctant to add more variants to the standard library. Then again if it contained a `fallbackdict' I wouldn't probably ever use `defaultdict' again. How often do you need to provide a factory?
3. Naming the other variant `defaultdict_const', `defaultdict_value', `defaultdict_whatever' beats the purpose because it's actually more characters to type than `defaultdict(lambda:', especially when you count the longer import name.
4. I cannot come up with another typical integer value that would be useful, then again I've used "", [] and set() numerous times. Adding zerodict, stringdict, listdict, setdict is obviously absurd.
5. The discussion started on Twitter amongst a couple of core devs and __missing__ didn't appear to be the one obvious way to anyone.
6. Of course I'm in no position to reject Guido's design on anything. Then again even `defaultdict(lambda:' is simply so much shorter than subclassing dict.

To sum up: if you don't find the idea of adding `fallbackdict' (possibly with an different *short* name) worth it, then I'm +1 on correcting the docs in terms of __missing__ and leaving the implementation as is.
msg122402 - (view) Author: Steve Holden (holdenweb) * (Python committer) Date: 2010-11-25 20:24
On 11/25/2010 1:44 PM, Łukasz Langa wrote:
> To sum up: if you don't find the idea of adding `fallbackdict'
> (possibly with an different *short* name) worth it, then I'm +1 on
> correcting the docs in terms of __missing__ and leaving the
> implementation as is.

+1

regards
 Steve
-- 
Steve Holden           +1 571 484 6266   +1 800 494 3119
PyCon 2011 Atlanta March 9-17       http://us.pycon.org/
See Python Video!       http://python.mirocommunity.org/
Holden Web LLC                 http://www.holdenweb.com/
msg122403 - (view) Author: Alex Gaynor (alex) * (Python committer) Date: 2010-11-25 20:25
I agree with Łukasz, it's more clutter than is worth for what amounts to: fallbackdict = lambda c, **kwargs: defaultdict(lambda c, **kwargs)

I will note, however, that almost all my use cases are with factories, primarily list set or int, and it was only this week that I first needed something else!
msg122404 - (view) Author: Éric Araujo (eric.araujo) * (Python committer) Date: 2010-11-25 20:32
> 4. I cannot come up with another typical integer value that would be
> useful, then again I've used "", [] and set() numerous times.
You can get '' with str as default factory and [] with list.

I think we agree on reclassifying this as a doc problem.  Proposal:

1) Add a small example in stdtypes.rst:dict section.

2) Add examples of giving dict or int to collections.defaultdict to get {} or 0 as default value.

3) Find a way to link to that example from the index.  It is not currently indexed because there is no dict.__missing__ method, hence no method markup that would trigger indexing.

4) Cross-link collections.rst:defaultdict, collections.rst:Counter and stdtypes.rst:__missing__ example.
msg122405 - (view) Author: Michael Foord (michael.foord) * (Python committer) Date: 2010-11-25 20:34
Well, I was perfectly aware of __missing__ - it's just a three liner to do it when using a lambda isn't *that* bad... I'm sure the documentation could be improved to highlight __missing__ though. It's almost always the case that documentation can be improved. :-)
msg122409 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2010-11-25 21:46
[Łukasz Langa]

> __missing__ didn't appear to be the one obvious way to anyone.

Two thoughts:

* There is part of the Zen that says that way may not be obvious unless your Dutch.  In this case,  __missing__ was the API designed by Guido to handle the problem.  If it isn't obvious, it is up to us to popularize the idiom in talks, in tutorials, in newsgroup discussions, etc.  And FWIW, it is not unprecedented -- Lua's dicts have a similar hook and most of the interesting programming in Lua relies on it.

* The __missing__ method should not be considered a dark corner of Python.  Unlike the copy module, pickling, of weakrefs, it is not tucked away in the library.  The missing method is part of the basic API for one of Python's most fundamental objects.  If someone is going to be a Python programmer, they must at least learn about dicts, lists, strings, and tuples.

> then again I've used "", [] and set() numerous times. 
> Adding zerodict, stringdict, listdict, setdict is 
> obviously absurd.

Looking at those examples, you're probably already aware that the list and set versions are already served by defaultdict(), and that it would be basic (and probably common) mistake to accidentally use [] in your proposed constant dict.

A constant version of the defaultdict only makes sense with immutables such as numbers, strings, and tuples.  The common case would be the number zero and we have Counter() for that.  So, you're left with very few use cases and with a hazard for users who may write: f = fallback_dict([]).   

> 4. I cannot come up with another typical integer value 
> that would be useful

FWIW, the Counter class *is* a ZeroDict.  It has a few extra methods but is basically a dict with __missing__ set to return zero.

> ... then I'm +1 on correcting the docs in terms of 
> __missing__ and leaving the implementation as is.

Thank you.  Will reclassify this as a doc issue.



[Éric Araujo]
> 2) Add examples of giving dict or int to 
> collections.defaultdict to get {} or 0 as default value.

Those examples have been there since day one.

[Michael Foord]
> I'm sure the documentation could be improved to highlight
> __missing__ though.

I'll add another example and perhaps include on in the tutorial.

> It's almost always the case that documentation can be improved. :-)

Getting people to read it is another story ;-)
msg125771 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2011-01-08 09:37
See r87858.
msg125811 - (view) Author: Éric Araujo (eric.araujo) * (Python committer) Date: 2011-01-09 00:06
Great, thanks.

We’ll see later if more cross-links are needed.
History
Date User Action Args
2011-01-09 00:06:05eric.araujosetnosy: rhettinger, holdenweb, eric.smith, eric.araujo, alex, michael.foord, docs@python, lukasz.langa
messages: + msg125811
stage: needs patch -> resolved
2011-01-08 09:37:02rhettingersetstatus: open -> closed

messages: + msg125771
resolution: fixed
nosy: rhettinger, holdenweb, eric.smith, eric.araujo, alex, michael.foord, docs@python, lukasz.langa
2010-11-25 21:46:06rhettingersetmessages: + msg122409
2010-11-25 20:34:47michael.foordsetmessages: + msg122405
2010-11-25 20:32:43eric.araujosettitle: defaultdict constructor with a concrete value -> Need example of using __missing__
components: + Documentation, - Library (Lib)

nosy: + docs@python
versions: + Python 3.1, Python 2.7
messages: + msg122404
stage: needs patch
2010-11-25 20:25:46alexsetnosy: + alex
messages: + msg122403
2010-11-25 20:24:07holdenwebsetmessages: + msg122402
2010-11-25 19:44:11lukasz.langasetmessages: + msg122399
2010-11-25 19:09:14rhettingersetkeywords: - easy
type: enhancement
messages: + msg122396
2010-11-25 18:54:29rhettingersetassignee: lukasz.langa -> rhettinger

nosy: + rhettinger
2010-11-25 17:55:14eric.araujosetnosy: + eric.araujo
messages: + msg122391
2010-11-25 17:54:32holdenwebsetmessages: + msg122389
2010-11-25 17:48:06eric.smithsetmessages: + msg122388
2010-11-25 17:36:50lukasz.langasetmessages: + msg122387
2010-11-25 17:31:15eric.smithsetnosy: + eric.smith
messages: + msg122386
2010-11-25 17:07:22michael.foordsetmessages: + msg122383
2010-11-25 17:00:47lukasz.langacreate