0

I have this code:

class Foo(object):
     def __getitem__(self, *args):
        print len(args), type(args)
        print args

This gives args as a tuple:

>>> x[1:]
1 <type 'tuple'>
(slice(1, None, None),)

But this gives args as a tuple of tuples:

>>> x[1:,2:,3:]
1 <type 'tuple'>
((slice(1, None, None), slice(2, None, None), slice(3, None, None)),)

Why so? I was expecting the last example to give me a tuple with three slice elements.

Zero Piraeus
  • 56,143
  • 27
  • 150
  • 160
Ankur Agarwal
  • 23,692
  • 41
  • 137
  • 208

1 Answers1

1

__getitem__'s function signature is:

def __getitem__(self, key):
    # ... etc.

Whenever you perform item access on an object, a single argument key is passed to __getitem__ (alongside the usual impled self). This is the case even if you write something like this:

obj[a,b,c]

The above will cause the single tuple (a, b, c) to be passed as the key argument to __getitem__() – it does not cause three arguments a, b and c to be passed.

Because a single key argument is always passed to __getitem__, the effect of argument unpacking in your malformed function signature __getitem__(self, *args) is that args will always be a tuple containing exactly one item (the key).

In the first case, since you used slice syntax, that key is a slice:

 |<- slice object ->|
(slice(1, None, None),)
|<----- 1-tuple ----->|

In the second case – x[1:,2:,3:] – your single key is a tuple of 3 slice objects, which due to your malformed function signature is then wrapped in a 1-tuple:

  |<- slice object ->|  |<- slice object ->|  |<- slice object ->|
 |<-------------------------- 3-tuple --------------------------->|
((slice(1, None, None), slice(2, None, None), slice(3, None, None)),)
|<--------------------------- 1-tuple ----------------------------->|

If you change your __getitem__ signature to the conventional __getitem__(self, key) form, you'll receive the tuple of three slice objects that you expect for your second example.

Zero Piraeus
  • 56,143
  • 27
  • 150
  • 160