Open justdoit0823 opened 7 years ago
If you want to control every next() called on the generator instead of passing it through using yield from you can use a decorator class to easily achieve this. Here's an example that allows you to limit the number of iterations in a generator:
class GenLimiter:
def __init__(self, func):
self.func = func
def __call__(self, *args, limit=-1, **kwargs):
self.limit = limit
self.gen = self.func(*args, **kwargs)
return self
def __iter__(self):
return self
def __next__(self):
if self.limit == 0:
raise StopIteration()
self.limit -= 1
return next(self.gen)
@GenLimiter
def forever():
num = 0
while True:
num += 1
yield num
# prints numbers 1-5
for item in forever(limit=5):
print(item)
We can easily write a decorator for simple function, as the following:
After, we can decorate other functions with it.
Run the function
foo
,But how about the generator function like the following?
Why the duration is zero?
The bar is a generator function, so calling the function will return a generator object, the generator function code actually doesn't execute, but decorator function has finished. So the duration is zero.
Why the result is right?
Because of the decorator function returning generator inside, and
tuple
executes generator function body and returns right result.Ah, how can we avoid this situation?
We can
yield from
the decorated function other than calling it, as the following:With this decorator, the result is correct.
How
yield from
works?When the underlying iterator is complete, the value attribute of the raised StopIteration instance becomes the value of the yield expression. It can be either set explicitly when raising StopIteration, or automatically when the sub-iterator is a generator (by returning a value from the sub-generator).
The full document is at generator-expressions.
The intresting Python, haha...