Builtin dict should be callable, since a dict defines a function
holger krekel
pyth at devel.trillke.net
Fri Dec 20 18:06:43 EST 2002
[Bengt]
> [holger]
> >> >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:
I didn't think that there could possibly be a difference
because __call__ and __getitem__ point to the same
object and are in the same class dict. So i thought
there shouldn't be any difference. So on to your tests :-)
> >>> 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
1) getitem: you resolve d.__getitem__ outside the
test (inside it just uses fast locals)
2) call: you resolve the __call__ method inside
the test (it has to be looked up!)
I changed your test but still __getitem__ is a lot faster
than __call__ (although they are bound to the same object).
This is probably because __getitem__ is specialized/optimized
which is also why it produces different bytecode.
> It looks like the class version runs 2-3 times slower than
> the direct method wrapper call, AFAICS
yes.
> >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 ;-)
this argument can be and has been turned against you ;)
> 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.
I guess that only different (needed/helpful) behaviour could possibly
convince people to make a change. Duplicating a name on one
of the fundamental types in python goes against the grain of the
language design (IMHO).
> It's not as if I'm proposing making (x, y, z)() == x(y, z)
> ;-)
regards,
holger
More information about the Python-list
mailing list