Builtin dict should be callable, since a dict defines a function
Bengt Richter
bokr at oz.net
Fri Dec 20 22:44:00 EST 2002
On 21 Dec 2002 00:25:25 +0100, martin at v.loewis.de (Martin v. =?iso-8859-15?q?L=F6wis?=) wrote:
>bokr at oz.net (Bengt Richter) writes:
>
>> >Your misconception is that you want to make one functional access to an
>> >object the preferred one. This is confusing:
>> Not to be confrontational, but what gives you the idea that I want
>> to make it preferred?
>
>Your message <atttql$ouf$0 at 216.39.172.122> indicated so. You wrote
>
># __call__(self, *args): return self[args]
>
>So you want d.__call__ to be the same as d.__getitem__. I was asking
(BTW actually the above is not the same due to single arg not getting
tuplified, see below.)
>why it shouldn't be
>
> def __call__(self, *args):
> return self.get(args)
>
Because get takes an extra argument and effectively extends the function
dynamically also if the default is not used. I am taking d[k] as the
defining function, being the simpler (considering exceptions to be exceptional ;-)
(In any case they're the same).
>I.e. d.__call__ = d.__get__
>
>Both are meaningful calling interfaces for the function defined by the
>dictionary, yet you prefer one of these two options over the other.
>
True.
>> Even so, I still don't see the objection to the nicer spelling,
>> since that syntax is not doing anything by default.
>
>My objection is that such a spelling should have an obvious meaning,
>i.e. that one should refuse the temptation to guess. It is not obvious
>to me why __getitem__ is better than get.
The point is for __call__ to do as default exactly what __getitem__ does.
What's to guess? (overriding is a different ball game).
>
>> One defines a (non-exception-raising) function mapping finite sets 1:1,
>> the other maps the rest of the universe to any selected element of the
>> universe as well. ISTM the simpler is the more natural default
>
>I question that the exception-raising one is the simpler one. From a
>functional point, exception raising is a difficult issue, functions
>are conceptually always total.
>
>> But I still would ask the question, "Why should an object which has an
>> unambiguous, simple, sensible, intuitively obvious functional
>> interpretation refuse to be called as a function (i.e., provide the
>> __call__ method wrapper) if this is not already defined ?"
>
>Can't answer this question in general. In the specific case, there is
>an easy answer: There is no unambiguous functional interpretation for
>dictionaries.
Well, there is in my mind ;-) Here's a go at a concrete representation
of an unambigous translation of d[x] to f(x), where f(x) does not depend
on d.__getitem__. I.e., it's really a function, just to show a concrete
function rendering of the same abstraction. And there is an unambiguous
(ISTM) 1:1 relationship.
Hence what makedfun returns when passed a dict is what I would say is an
unambiguous functional interpretation for the given dictionary.
It defines the *behavior* I was proposing (albeit without len(args)==1 test)
for default dict.__call__ behaviour (though I'm certainly not proposing
this implementation ;-))
Now pretend this _function_ is bound to __call__. Then optimize. Will
you not get d.__call__ == d.__getitem__ ?
====< makedefun.py >=========================================
def makedfun(dct):
keys, values = map(list,zip(*dct.items()))
def dictfun(*key):
if len(key)==1: key=key[0]
if key not in keys: raise KeyError, 'dictfun KeyError: %s' % key
i = keys.index(key)
return values[i]
return dictfun
def test(d):
dfoo = makedfun(d)
for k,v in d.items():
print 'k=%s, d[%s]=%s, dfoo[%s]=%s' % (k,k,d[k],k,dfoo(k))
if __name__=='__main__':
test(dict([(x,chr(x)) for x in range(ord('a'),ord('e')+1)]))
test({1:'one',2:'two',(1,2):'tup12',(3,4):'tup34',(1,):'tup1'})
print 'Should be 1:',makedfun({'x':1})('x'),"Next should fail with 'y' key"
try:
print makedfun({'x':1})('y')
except KeyError, e:
print e
else:
print "Failed to fail on key 'y'"
=============================================================
Output:
k=97, d[97]=a, dfoo[97]=a
k=98, d[98]=b, dfoo[98]=b
k=99, d[99]=c, dfoo[99]=c
k=100, d[100]=d, dfoo[100]=d
k=101, d[101]=e, dfoo[101]=e
k=(1,), d[(1,)]=tup1, dfoo[(1,)]=tup1
k=1, d[1]=one, dfoo[1]=one
k=2, d[2]=two, dfoo[2]=two
k=(1, 2), d[(1, 2)]=tup12, dfoo[(1, 2)]=tup12
k=(3, 4), d[(3, 4)]=tup34, dfoo[(3, 4)]=tup34
Should be 1: 1 Next should fail with 'y' key
dictfun KeyError: y
If you want to, you can pick a nit with the equivalent results
of dfun() and dfun(()) ;-)
>>> import makedfun
>>> dfun = makedfun.makedfun({1:'one',():'tempty tuple'})
>>> d = {1:'one',():'tempty tuple'}
>>> dfun()
'tempty tuple'
>>> dfun(())
'tempty tuple'
>>> d[()]
'tempty tuple'
>>> d[]
File "<stdin>", line 1
d[]
^
SyntaxError: invalid syntax
Regards,
Bengt Richter
More information about the Python-list
mailing list