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?

3
  • Just asking this, and answering it, in case someone else needs to know how to do this. Commented May 29, 2009 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? Commented May 29, 2009 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. Commented May 29, 2009 at 18:29

2 Answers 2

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
Sign up to request clarification or add additional context in comments.

2 Comments

+1 for the proper use of new-style classes (as well as the overall goodness of the approach).
Sadly, this makes isinstance(a, collections.Iterable) return True :(
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

2 Comments

If there is a more pythonic answer than this I would gladly accept it.
have you class name properly capitalised, that'd be more pythonic

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.