I am studying python with the book Beginning Python: From Novice to Professional , and I get confused about the section discussing iterators
. There is an example in this section:
>>> Class Fibs:
... def __init__(self):
... self.a = 0
... self.b = 1
... def __next__(self):
... self.a, self.b = self.b, self.a + self.b
... return self.a
... def __iter__(self):
... return self
...
>>> fibs = Fibs()
>>> for f in fibs:
... if f > 1000:
... print(f)
... break
...
1597
To be honest, I only know that fibs
is an object with methods __next__
and __iter__
, but have no idea about what happens in each step of the loop. And I made a test:
>>> isinstance(f, Fibs)
False
>>> f is fibs
False
>>> isinstance(f, int)
True
>>> fibs.a
1597
This makes me much more confused! Why the boolean value of f is fibs
is False
? And why fibs.a
become 1597 after the execution of the loop?(Is the method __next__
automatically called in the loop?) Thanks in advance.
1 fibs = Fibs()
2 for f in fibs:
3 if f > 1000:
4 print(f)
5 break
Line 1 creates a Fibs() object, calling __init__()
. Line 2 calls __iter__()
, which returns an iterator object (in this case, just fibs itself). The interpreter will then begin calling the __next__()
method repeatedly; it returns self.a
, a plain number, which is assigned to the loop variable f
(so of course it's not a Fibs() object and certainly not the one named fibs). When that value reaches 1000, the if clause will fire, print the result, and break out of the loop.
And why fibs.a become 1597 after the execution of the loop?
Well this is because it is going through the Fibonacci sequence and this is the first number over 1000 in the sequence.
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597 , 2584 ...
On enter to the loop interpreter calls __iter__
method. On every loop step interpreter calls __next__
method of Fibs
.
Yes, the loop automatically calls __next__
.
The for
loop does this to the given object:
# turn the object into an iterator
iterator = iter(given_object)
while True:
try:
# try to get the next value
next_value = next(iterator)
except StopIteration
# for loop is done, run the `else:` block if there is any
run_for_else_suite()
break
else:
# set the loop target to `next_value`
assign_next_value(next_value)
continue = run_loop_body()
if not continue:
break
The iter()
function calls given_object.__iter__()
, and the next()
function calls given_object.__next__()
; these functions offer some extra functionality and are the proper way of calling into the iterator API.
So, in each loop iteration, f
is assigned the value that the Fib.__next__
method returned.
You can also see that Fib
is its own iterator; __iter__
returns self
. Other types can return a dedicated iterator object; lists do, for example:
>>> iter([])
<listiterator object at 0x129516610>
Returning dedicated iterators lets you create multiple 'views' on an object where each iterator maintains its own position, like nested loops:
lst = [1, 2, 3]
for i in lst:
for j in lst;
print (i, j) # prints (0, 0), (0, 1), (0, 2), (0, 3), (1, 0), etc.
or you can explicitly reuse the iterator:
lst = [1, 2, 3]
lst_iter = iter(lst)
for i in lst_iter:
for j in lst_iter:
print (i, j) # prints (0, 1), (0, 2)
for f in fibs
implicitly calls fibs.__iter__
and binds f
to all the objects that it yields, in turn. None of these objects are instances of Fibs
, let alone equal to fibs
. The whole is (roughly) equivalent to
# boilerplate, implicit in the "for" notation
__iterator = iter(fibs)
while True:
try:
f = next(__iterator)
except StopIteration:
break
# your code, with f now bound to what the iterator yielded
if f > 1000:
print(f)
break
( iter
and next
are the clean ways to call __iter__
and __next__
, respectively.)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.