[Tutor] overriding brackets in lvalue assignment possible?

Peter Otten __peter__ at web.de
Tue Dec 27 12:13:16 EST 2016

James Hartley wrote:

> I can successfully override __getitem__() for rvalues, but the following
> example shows that more is required when used as an lvalue:
> ===8<-----
> #!/usr/bin/env python
> class Foo():
>     def __init__(self, n):
>         self.d = dict.fromkeys([i for i in range(0, n)])
>     def __getitem__(self, i):
>         return self.d[i]
> def main():
>     foo = Foo(4)
>     print(foo[0])
>     foo[0] = 2               # not as an lvalue?
>     print(foo[0])
> if __name__ == '__main__':
>     main()
> ===8<-----
> Python 3.4 generates the following output when this example is executed:
> None
> Traceback (most recent call last):
>   File "./test.py", line 17, in <module>
>     main()
>   File "./test.py", line 13, in main
>     foo[0] = 2
> TypeError: 'Foo' object does not support item assignment
> I am surprised that the error states that the object itself cannot accept
> assignment.  From the C++ perspective, the underlying dictionary should be
> exposed.  Does Python overloading allow use of bracket overriding in
> lvalues?

Since Python doesn't allow multiple method definitions with different 
signatures under the same name there's a dedicated __setitem__() method:

>>> class Foo:
...     def __setitem__(self, index, value): print("Setting Foo()[{!r}] to 
{!r}".format(index, value))
>>> foo = Foo()
>>> foo[42] = "bar"
Setting Foo()[42] to 'bar'

Of course with the above definition

>>> foo[42]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'Foo' object does not support indexing

but you already know how to fix that. To get a class that works like 
Python's list or dict while only writing a few methods yourself you can 
inherit from collections.MutableSequence or MutableMapping.

PS: Even without function overloading the developers might have chosen a 
common method for both, e. g.

def __item__(self, index, *args):
   if args:
       [value] = args
       # set item
       result = ... # calculate current value
       return result

The actual choice is cleaner and a tad more efficient.

More information about the Tutor mailing list