Search code examples
pythonpython-2.7sliceargument-unpacking

Tuple of tuple given on slice generation


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.


Solution

  • __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.