rassar
rassar

Reputation: 5680

overloaded __getitem__ accepting another argument

I want to make the __getitem__ of one class be the sum of it and the item at the same index of another list.

>>> class Test(list):
    def __init__(self):
        self.extend([1, 2, 3])
    def __getitem__(self, index, other):
        return self[index] + other[index]
>>> t = Test()
>>> t2 = [4, 5, 6]

However, my two attempts have resulted in errors:

>>> t[5, t2]
Traceback (most recent call last):
  File "<pyshell#24>", line 1, in <module>
    t[5, t2]
TypeError: __getitem__() missing 1 required positional argument: 'other'
>>> t.__getitem__(5, t2)
Traceback (most recent call last):
  File "<pyshell#26>", line 1, in <module>
    t.__getitem__(5, t2)
  File "<pyshell#17>", line 5, in __getitem__
    return self[index] + other[index]
TypeError: __getitem__() missing 1 required positional argument: 'other'

Is it possible to give __getitem__ multiple arguments? If so, how? If not, is there a way to emulate it?

Upvotes: 4

Views: 1750

Answers (2)

MSeifert
MSeifert

Reputation: 152795

It is possible because you get a tuple of "indices" in __getitem__ not multiple parameters:

class Test(list):
    def __init__(self):
        self.extend([1, 2, 3])

    def __getitem__(self, value):
        # this makes sure that a sequence with exactly 2 elements is passed in: (thanks @ShadowRanger)
        index, other = value  
        return super().__getitem__(index) + other[index]

>>> t = Test()
>>> t2 = [4, 5, 6]
>>> t[2, t2]
9   # 3 + 6
>>> t[1, t2]
7   # 2 + 5
>>> t[0, t2]
5   # 1 + 4

There are some caveats however:

  • You need to explicitly call super().__getitem__ so you don't end up in an recursion.
  • You have to pay attention to the number of arguments passed to your __getitem__ if you want to allow normal slicing.
  • This behaviour will confuse users, so I wouldn't actually recommend using it!

Upvotes: 4

Chris
Chris

Reputation: 22993

Is it possible to give getitem multiple arguments? If so, how? If not, is there a way to emulate it?

Why would want to emulate this? You're missing the point of "magic methods". Python provides you with these methods to give a form of operator overloading. Here is an excerpt from the Python documentation section 3.3. Special method names, that describes what "magic methods" are meant to be used for:

A class can implement certain operations that are invoked by special syntax (such as arithmetic operations or subscripting and slicing) by defining methods with special names. This is Python’s approach to operator overloading, allowing classes to define their own behavior with respect to language operators.

(emphasis mine)

Furthermore, this makes your class ambiguous to readers of your code who are expected __getitem__ to return a single element.

Since @MSeifert has already given you a way to achieve the behavior you want, I won't post my solution. But I strongly encourage you to create your own custom method. If you still do choose to make changes to the functionality of your __getitem__ method, I highly recommend that you at least document these changes and make it clear that your implementation of __getiem__ has different behavior than the normal implementation.

Upvotes: 1

Related Questions