3

I want to define a class that supports __getitem__, but does not allow iteration. for example:

class B:
   def __getitem__(self, k):
      return k

cb = B()

for x in cb:
   print x

What could I add to the class B to force the for x in cb: to fail?

ecatmur
  • 152,476
  • 27
  • 293
  • 366
grieve
  • 13,220
  • 10
  • 49
  • 61
  • Just asking this, and answering it, in case someone else needs to know how to do this. – grieve May 29 '09 at 15:49
  • 3
    Out of curiosity, *why* do you want to give access to getitem but not make it iterable? What was your use-case? – Jon Cage May 29 '09 at 15:52
  • 1
    I have a class which acts like, but does not inherit from, a dictionary. Therefore I define __getitem__, and if someone tries to iterate over it, I want it to error, rather than start trying to call __getitem__ with whole numbers. Just to be clear, this is not how I would have chosen to implement this particular class, but my decision was overridden. – grieve May 29 '09 at 18:29

2 Answers2

14

I think a slightly better solution would be to raise a TypeError rather than a plain exception (this is what normally happens with a non-iterable class:

class A(object):
    # show what happens with a non-iterable class with no __getitem__
    pass

class B(object):
    def __getitem__(self, k):
        return k
    def __iter__(self):
        raise TypeError('%r object is not iterable'
                        % self.__class__.__name__)

Testing:

>>> iter(A())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'A' object is not iterable
>>> iter(B())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "iter.py", line 9, in __iter__
    % self.__class__.__name__)
TypeError: 'B' object is not iterable
Rick Copeland
  • 11,672
  • 5
  • 39
  • 38
2

From the answers to this question, we can see that __iter__ will be called before __getitem__ if it exists, so simply define B as:

class B:
   def __getitem__(self, k):
      return k

   def __iter__(self):
      raise Exception("This class is not iterable")

Then:

cb = B()
for x in cb: # this will throw an exception when __iter__ is called.
  print x
Community
  • 1
  • 1
grieve
  • 13,220
  • 10
  • 49
  • 61