I am working on Python 2.7 and I am trying to overload __getitem__
and __setitem__
from a class that inherits list
.
Let's say I have this class A
:
class A(list):
def __getitem__(self, key):
print "GET!"
def __setitem__(self, key, value):
print "SET!"
With square brackets, A.__getitem__
or A.__setitem__
should be called. Normally it is like that, but when I use [:]
the parent implementation is called instead. Why? And why does [::]
work?
a = A([1])
a[1] # prints GET!
a["1"] # prints GET!
a[::] # prints GET!
a[slice(None)] # prints GET!
a[:] # returns the list [1]
And the same with __setitem__
:
a[1] = 2 # prints SET!
a[::] = 2 # prints SET!
a[slice(None)] = 2 # prints SET!
a[:] = [2] # changes the list
That's because in Python 2 [1][:]
as well as 1-d slices with start and/or end (but not when step is specified) like [1:]
, [:3]
, or [1:3]
go through __getslice__
and __setslice__
if they are implemented. If they are not implemented they will also go to __getitem__
and __setitem__
). Quoting from the docs:
Notice that these methods [
__*slice__
] are only invoked when a single slice with a single colon is used, and the slice method is available. For slice operations involving extended slice notation, or in absence of the slice methods,__getitem__()
,__setitem__()
or__delitem__()
is called with a slice object as argument.
In your case you inherit them from list
(list
implements them) so it bypasses your __getitem__
and __setitem__
in simple slice situations.
As an example, you could override the __*slice__
methods to verify that the [:]
call really goes there:
class A(list):
def __getitem__(self, key):
print "GET!"
def __setitem__(self, key, value):
print "SET!"
def __getslice__(self, i, j):
print "GETSLICE!"
def __setslice__(self, i, j, seq):
print "SETSLICE!"
However these are only called when just one slice is passed in and only if the passed slice doesn't have a step. So [::]
won't go there because it has a step (even though it's implicit). But also [:,:]
wouldn't go into these because it is translated to tuple(slice(None), slice(None))
which isn't a simple slice but a tuple of slices. It also doesn't go into these __*slice__
methods if you pass in a slice
instance yourself, that's why [slice(None)]
even though seemingly equivalent to [:]
directly goes into __*item__
instead of __*slice__
.
[1] In Python 3 the __*slice__
methods were removed so there the [whatever]
indexing will go to the __*item__
methods.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With