__setitem__ puzzle

Terry Reedy tjreedy at udel.edu
Mon Apr 21 20:20:28 CEST 2003

"Anton Vredegoor" <anton at vredegoor.doge.nl> wrote in message
news:b80q7c$cmd$1 at news.hccnet.nl...
> I have subclassed the builtin list type to accept tuples for

As written (and modified by me), it *requires* a sequence for
(To relax this, one would have to text index for type==int and branch
As Aahz correctly said, you should specify what l[tuple] is supposed
to mean.
Reading code and usage reveals the intention that
l[(a,b,c)]==l[a][b][c], etc.

> class tlist(list):
>     def __getitem__(self,x):
>         y = self[:]

This is your problem.  By itself, making a shallow copy wastes time
and space.

>         for z in x:
>             y = y[z]

The reason for the copy is to upcast self from a tlist to a list so
that y[x] calls list.__getitem__ instead of recursively calling this
method with x not a sequence.  I believe this would fail anyway if any
of the sublists of self were tlists themselves instead of lists.

>         return y
>     def __setitem__(self,x,val):
>         y = self[:]

Here the copying is fatal when len(x)==1

>         for z in x[:-1]:
>             y = y[z]
>         y[x[-1]] = val

because then you only change the temporary instead of self itself.

>     L[(1,1)] = 0
>     print L
>     #this doesn't work

'this' is ambiguous -- line above or below?  (ans.: below)
so is "doesn't work" -- wrong answer or exception? (ans.: wrong
answer, I think)

>     L[(1,)] = 0

As explained above, L is not modified, just the internal copy.

The solution is to directly do what you tried to do indirectly by
making the copy.
This also works with tlists as sublists.

class tlist(list):
    def __getitem__(self, x):
        lget = list.__getitem__
        y = self
        for z in x:
            y = lget(y, z)
        return y
    def __setitem__(self, x, val):
        lget = list.__getitem__
        y = self
        for z in x[:-1]:
            y = lget(y, z)
        list.__setitem__(y, x[-1], val)

def test():
    L = tlist(['aa',['bb',tlist(['cc','dd']),'ee'],'ff',['gg']])
    print L
    print L[(1,1,0)]
    L[(1,1,0)] = 0
    print L
    L[(1,)] = 0
    print L

>>> test()
['aa', ['bb', ['cc', 'dd'], 'ee'], 'ff', ['gg']]
['aa', ['bb', [0, 'dd'], 'ee'], 'ff', ['gg']]
['aa', 0, 'ff', ['gg']]

Terry J. Reedy

More information about the Python-list mailing list