Builtin dict should be callable, since a dict defines a funct ion

Terry Reedy tjreedy at udel.edu
Sat Dec 21 12:27:50 EST 2002


"Bengt Richter" <bokr at oz.net> wrote in message
news:au22dd$jpi$0 at 216.39.172.122...
> If I know that a dict will have a _default_ function-style interface
> as if its class were defined with __call__ = __getitem__, then I can
> make an anonymous collection of function and dict objects, e.g., a
list
>
>   fdmix = [f1,f2,d1,d2]
>
> and depend on the callable assumption for all elements.
>
> Now what power does this give me over the following list?
>
>   fdmix2 = [f1,f2,d1.__getitem__, d2.__getitem__]
>
> Well, this list does not carry the same information. I have been
> forced to throw away the possibility of distinguishing and acting
> differently based on type _if_ that is useful for another use of
> the list (e.g., passing the collection to different places).
>
> I admit that a subclass will do what I want, but there is a
performance
> hit for some reason (see below).

Since I disagree that you made the proper comparison (see below), I
disagree with the conclusion.

>
> Note that speed could make a lot of difference if you were passing a
function
> that processed pixels for example:

For speed, pixel processing should be done in C.

>  >>> import timefuns
>  >>> def noop(k): pass
>  ...
>  >>> d={'xx':123}
>  >>> class D(dict): __call__ = dict.__getitem__
>  ...
>  >>> dc = D(d)
>  >>> df = d.__getitem__
>  >>> dc.__name__ = 'D inst'
>  >>> calls = [(noop,'xx'),(dc,'xx'),(df,'xx')]
>  >>> timefuns.timefuns(100000, calls)
>             timing oh:  0.000015  ratio
>                  noop:  0.000009   1.00
>                D inst:  0.000012   1.29
>           __getitem__:  0.000004   0.48
>
> Timing overhead is time measured by t1-t0 in
>    t0=time.clock()
>    t1=time.clock()
> where otherwise there is a call to the function as a single line
> in between, in thefunction timing loop.
>
> This overhead is subtracted out to get a better relative time
between
> the functions. The ratios are wrt to the first function in the list.
>
> Note that that d.__getitem__ is almost twice as fast as even just
> calling a noop function with the same single parameter.
>
> So I think there is more than one argument in favor of this simple
> backwards-compatible change ;-)

I think there is a mis-comparison here.  You are asking that dicts be
augmented to look like your class D, so that d['a'] == d('a').
However, your 'test' above compares d('a') to d.__getitem__('a'), not
d['a'], avoiding most overhead, and finds the latter faster.  So what?
If you got the augmentation, it would run at the slower speed, due to
the need to look up .__call__().  So this result does not support your
proposal.  I could even see it as an argument against.

What you have shown (thank you) is that the exposure of internal
methods that came with new-style classes lets us speed up dict lookups
by bypassing the overhead of interpreting  the d[key] syntax, which
has to start with determining the type(d) (seq or dict?).  Replacing
d[key] with d.__getitem__(key) (where d__getitem__ is looked up just
once) implicitly 'declares' the type of d within the loop by bypassing
repeated lookup of its type.

Terry J. Reedy





More information about the Python-list mailing list