9

If I define the __iter__ method as follows, it won't work:

class A:

    def __init__(self):
        self.__iter__ = lambda: iter('text')


for i in A().__iter__():
    print(i)

iter(A())

Result:

t
e
x
t
Traceback (most recent call last):
  File "...\mytest.py", line 10, in <module>
    iter(A())
TypeError: 'A' object is not iterable

As you can see, calling A().__iter__() works, but A() is not iterable.

However if I define __iter__ for the class, then it will work:

class A:

    def __init__(self):

        self.__class__.__iter__ = staticmethod(lambda: iter('text'))
        # or:
        # self.__class__.__iter__ = lambda s: iter('text')


for i in A():
    print(i)

iter(A())

# will print:
# t
# e
# x
# t

Does anyone know why python has been designed like this? i.e. why __iter__ as instance variable does not work? Don't you find it unintuitive?

3
  • I think the way you try to do it in very unintuitive, why don't you just override iter as a method ? stackoverflow.com/questions/4019971/… Commented Apr 11, 2017 at 13:02
  • 1
    Seems to be an old-style vs new-style class issue. Your code does not work with new-style classes. Commented Apr 11, 2017 at 13:21
  • @PyNico In the particular case that I'm currently working on, I prefer to define __iter__ inside __init__ because it's implementation depends on the instantiation arguments. Commented Apr 11, 2017 at 13:31

1 Answer 1

6

It is done by design. You can find the thorough description here: https://docs.python.org/3/reference/datamodel.html#special-method-lookup

Short answer: the special method must be set on the class object itself in order to be consistently invoked by the interpreter.

Long answer: the idea behind this is to speed up well-known constructions. In your example:

class A:
    def __init__(self):
        self.__iter__ = lambda: iter('text')

How often are you going to write a code like this in real life? So, what Python does - it skips a dictionary lookup of the instance, i.e. iter(A()) simply does not "see" that self.__iter__, which is actually self.__dict__['__iter__'] in this case.

It also skips all the __getattribute__ instance and metaclass lookup gaining a significant speedup.

Sign up to request clarification or add additional context in comments.

2 Comments

I've rewritten the answer to make it more comprehensible. Please tell if you have any questions, or something is not clear.
This is probably also related to why (1, 2, 3)[2] is much more faster than direct calling of (1, 2, 3).__getitem__(2) (in the latter python also looks inside instance attributes). Great answer, thank you so much.

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.