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

Bengt Richter bokr at oz.net
Fri Dec 20 13:28:11 EST 2002


On Fri, 20 Dec 2002 11:54:28 +0100, holger krekel <pyth at devel.trillke.net> wrote:

>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 wondered, so I tested:
 >>> from time import clock
 >>> def test(f, n):
 ...     t12=t23=0.0
 ...     for i in xrange(n):
 ...         t1=clock()
 ...         y=f('x')
 ...         t2=clock()
 ...         t3=clock()
 ...         t12+=t2-t1
 ...         t23+=t3-t2
 ...     return (t12-t23)/n
 ...
 >>> class DSC(dict):
 ...     __call__ = dict.__getitem__
 ...
 >>> d={'x':1}
 >>> dsc=DSC()
 >>> dsc['x']=1
 >>> d
 {'x': 1}
 >>> dsc
 {'x': 1}
 >>> dmw=d.__getitem__
 >>> test(dsc,100000)
 9.4262484684884387e-006
 >>> test(dmw,100000)
 3.5901983099864767e-006
 >>> test(dsc,100000)
 9.4990956953597565e-006
 >>> test(dmw,100000)
 3.7816695190474548e-006

It looks like the class version runs 2-3 times slower than
the direct method wrapper call, AFAICS

>
>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.  
>
Well, that's one way to look at it, but not the only one ;-)

>Maybe it makes sense to implement a different __call__ method:
If it does, you can always subclass and override. The good old
__getitem__ interface will still be there ;-)
These are interesting.
>
>    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:
Ditto
>
>    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. 
Well, I think these are all interesting variations, but I don't
think any qualifies as a uniquely simple and unambiguous semantic
for a dict, whereas I think the simple 1:1 key:value mapping function
does. I think that the index:value mappings of lists and tuples also
define simply understood functions, and I would like the unifying alias
of a default __call__ for those analogously, while recognizing that
I can use obj.__getitem__ now.

It's not as if I'm proposing making  (x, y, z)() == x(y, z)
;-)

Regards,
Bengt Richter



More information about the Python-list mailing list