Search code examples
pythonargumentsargument-unpacking

How to overload * argument unpacking operator?


I have data like data = [[t1, t2, ...], [v1, v2, ...]]. I want to wrap this in a class so I can call data.t instead of having to use data[0].

I tried to do this with the following:

class Variable:
    def __init__(self, data):
        self.t = data[0]
        self.v = data[1]

    def __getitem__(self, key):
        if key == 0:
            return self.t
        elif key == 1:
            return self.v
        else:
            raise ValueError("not valid key '{}'".format(key))

    def __setitem__(self, key, value):
        if key == 0:
            self.t = value
        elif key == 1:
            self.v = value
        else:
            raise ValueError("not valid key '{}'".format(key))

The reason for the __getitem__ and __setitem__ overloading is for backwards compability so that data[0] still works. This works for most things, however I run into problems with the following call:

func_that_takes_two_arguments(*data) # unpacking data

The error I get is

/Users/pingul/Workspace/lhcfill/oml.py in __getitem__(self, key)
     52                                 return self.val
     53                         else:
---> 54                                 raise ValueError("not valid key     '{}'".format(key))
     55 
     56                 def __setitem__(self, key, value):
ValueError: not valid key '2'

How can I make my class work properly with the argument unpacking operator?


Solution

  • The * operator works by iterating over the object. This iteration can well be performed with only implementing __getitem__(), but your implementation is faulty. Instead if raising ValueError, you should throw IndexError which signals the end of the iteration.

    See also https://docs.python.org/3/reference/datamodel.html#object.getitem which explicitly states

    Note: for loops expect that an IndexError will be raised for illegal indexes to allow proper detection of the end of the sequence.

    https://docs.python.org/2/library/functions.html#iter states that this is called the "sequence protocol".