Builtin dict should be callable, since a dict defines a function

holger krekel pyth at devel.trillke.net
Fri Dec 20 05:54:28 EST 2002


Bengt Richter wrote:
> On Thu, 19 Dec 2002 18:17:03 -0800, Erik Max Francis <max at alcyone.com> wrote:
> 
> >Bengt Richter wrote:
> >
> >> I posted the suggestion in a thread  Re: case-insensitive and
> >> internationalized sort,
> >> but it occurs to me that the principle is arguable from the abstract
> >> point of view.
> >> I.e., a dict implements a function key -> value, so why not let it
> >> accept a normal
> >> function arg list (i.e., the tuple) as the argument for its key ->
> >> value function?

I use this idiom sometimes.  From a mathematical point of view,
functions define mappings/relations between keys and values. 
So your idea makes sense to me (and may not even be marginal :-)

> >> It should be a pretty simple change to provide __call__(self, *args):
> >> return self[args]
> >> under the hood, and let hashability chips fall where they will via [].
> >
> >But why would this be helpful?  A dictionary implements a mappable
> >interface; a function implements are callable interface.  They're
> >different interfaces; why would dovetailing them into the same interface
> >be beneficial?
> You can pass a reference to a dict to something that expects a function,
> e.g., a sort. You could pass it as a memoized-result proxy function in
> some context and catch KeyError to take care of LRU caching update, etc.
> I'm sure many uses would turn up once the obstacle was removed, and
> people thought of functions as mappings and vice versa ;-)

right, so the key point is *thinking* it this way. 

> >It's a simple change, sure, but furthermore it's trivial enough for you
> >to implement it yourself in a subclass of dict.
> Yes (as I mentioned less specifically ;-) But not without the performance
> hit of an extra layer.

class fdict(dict):
    __call__ = dict.__getitem__

no performance hit at all.

I am not sure that it is a good idea to put this into the python dict
implementation because it would provide *two* obvious ways to use 
a dict-mapping.  

Maybe it makes sense to implement a different __call__ method:

    def __call__(self, *args):
        for key in args:
            yield self[key]

so that you can get to multiple results with one call.  
There are certainly a lot of variations/additions possible:

    def __call__(self, *args):
        for key in args:
            if isinstance(key, list):
                yield map(self.__getitem__, key)
            else:
                yield self[key]

which would essentially allow 

>>> a=fdict(zip(range(0,10),range(10,20)))
>>> a([1,2])
<generator object at 0x824e040>
>>> a([1,2]).next()
[11, 12]
>>> map(None, a([1,2], 3,4))
[[11, 12], 13, 14]
>>> v1,v2,v3 = a(1,2,3)

Now the real work is to figure out which __call__ semantics 
would satisfy a real need or be really simplifying in everyday
work. 

cheers,

    holger




More information about the Python-list mailing list