Argument unpacking with custom __getitem__ method never terminates

Viewed 102

Could someone explain what is going on under the hood and why this program does not finish?

class A:
    def __getitem__(self, key):
        return 1

print(*A())
2 Answers

This program doesn't finish because the class you defined is iterable, using the old sequence iteration protocol. Basically, __getitem__ is called with integers increasing from 0, ..., n until an IndexError is raised.

>>> class A:
...     def __getitem__(self, key):
...         return 1
...
>>> it = iter(A())
>>> next(it)
1
>>> next(it)
1
>>> next(it)
1
>>> next(it)
1

You are unpacking an infinite iterator, so eventually you'll run out of memory.

From the iter docs:

Without a second argument, object must be a collection object which supports the iteration protocol (the __iter__() method), or it must support the sequence protocol (the __getitem__() method with integer arguments starting at 0). If it does not support either of those protocols, TypeError is raised.

According to the documentation of object.__getitem__ it is supposed to raise IndexError or KeyError when a non-existent elements is requested.

You ignore the key and return a constant—so you never indicate that it is finished.

class A:
  n = 0
  def __getitem__(self, _):  # ignoring the key
     A.n += 1
     if A.n > 1000:
         raise IndexError()
     return A.n

print(*A())

will finish and print up to 1000 - yours never does - when decompositioning your class you are never done.

Output:

1 2 3 4 5 ... 998 999 1000
Related