Reputation: 1099
So I understand that sometimes instead of defining iter and next methods within a class that is supposed to be iterable, using just an iter method containing a yield statement suffices. Actually why? Just do avoid boilerplate code?
However, I dont get why the following snippet yields three iterations
class BoundedRepeater:
def __init__(self, value, max_repeats):
self.value = value
self.max_repeats = max_repeats
self.count = 0
def __iter__(self):
return self
def __next__(self):
if self.count >= self.max_repeats:
raise StopIteration
self.count += 1
return self.value
if called like this
for item in BoundedRepeater("Hello", 3):
print(item)
but if I change the methods to
class BoundedRepeater: def init(self, value, max_repeats): self.value = value self.max_repeats = max_repeats self.count = 0
class BoundedRepeater:
def __init__(self, value, max_repeats):
self.value = value
self.max_repeats = max_repeats
self.count = 0
def __iter__(self):
if self.count >= self.max_repeats:
raise StopIteration
self.count += 1
yield self.value
I only get one iteration instead of three
Upvotes: 0
Views: 152
Reputation: 2705
The second example gives you only one iteration because the yield
statement is called only once, because __iter__
is called only once. The __iter__
method does not return the next value (and therefore is not called once per value), it returns an iterator that has all the values.
You could write
class SmallPrimes:
def __iter__(self):
yield 2
yield 3
yield 5
yield 7
which shows that one single call to iter
contains all the values.
In your case, the BoundedRepeater
class would put the yield
in a for
-loop:
class BoundedRepeater:
def __init__(self, value, max_repeats):
self.value = value
self.max_repeats = max_repeats
def __iter__(self):
for _ in range(self.max_repeats):
yield self.value
The execution of __iter__
is paused during each yield, and then resumes when the next value is required. During that pause, all the context is kept. Note how there is no need to keep track of a counting variable (self.count
in your first example).
For your specific example, which is about repeating a value V a number N of times, you do not need to write your own class. You can just write a function:
def bounded_repeat(V, N):
for _ in range(N):
yield V
for v in bounded_repeat(V, N):
y = do_something(v)
But you do not need to write that function because it already exists:
import itertools
for v in itertools.repeat(V, N):
y = do_something(v)
However, unless V is heavy and N is huge, just stick to some good old Python:
for v in [V] * N:
y = do_something(v)
Upvotes: 2
Reputation: 439
You can use a generator function, but you should yield results in a loop, in your case bounded by the number max_repeat
I think your __iter__
method should be written as follow:
def __iter__(self):
for i in range(self.max_repeats):
yield self.value
return
I tried and it generates as many items as specified in max_occurences.
br = BoundedRepeater('tagada', 5)
for item in br:
print(item)
# ->tagada
# ->tagada
# ->tagada
# ->tagada
# ->tagada
Upvotes: 0