With list comprehensions, we can directly create a list. However, due to memory constraints, the list capacity is definitely limited. Moreover, creating a list with 1 million elements not only takes up a lot of storage space, but if we only need to access the first few elements, then most of the space occupied by the latter elements will be wasted.
So, if the elements of the list can be calculated according to a certain algorithm, can we continue to calculate the subsequent elements in the process of looping? This saves a lot of space by not having to create a complete list. In Python, this mechanism of computing while looping is called a generator: generator.
>>> L = [x * x for x in range(10)]
>>> L [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] >>> g = (x * x for x in range(10))
As we said, the generator saves the algorithm. Each time it is called next(g)
, it calculates the g
value of the next element, until the last element is calculated, and StopIteration
an error is thrown when there are no more elements.
Generators are also iterables:
>>> g = (x * x for x in range(10))
>>> for n in g: ... print(n) ... 0 1 4 9 16 25 36 49 64 81
If the calculation algorithm is complex and for
cannot be implemented by a loop similar to list generation, it can also be implemented by a function.
For example, in the famous Fibonacci sequence, any number except the first and second numbers can be obtained by adding the first two numbers:
1, 1, 2, 3, 5, 8, 13, 21, 34, ...
The Fibonacci sequence cannot be written with list comprehension, but it is easy to print it with a function:
def fib(max):
n, a, b = 0, 0, 1 while n < max: print(b) a, b = b, a + b n = n + 1 return 'done'
The above functions and generators are just one step away. To turn fib
a function into a generator, just print(b)
change yield b
it to:
def fib(max):
n, a, b = 0, 0, 1 while n < max: yield b a, b = b, a + b n = n + 1 return 'done'
This is another way to define a generator. If a function definition contains yield
keywords, then the function is no longer a normal function, but a generator
Here, the most difficult thing to understand is that the execution flow of generator and function is different. Functions are executed sequentially, return
returning when a statement or the last line of a function statement is encountered. The function that becomes the generator is next()
executed every time it is called, and the statement is returned when it encounters yield
a statement, and it continues to execute from the last returned yield
statement when it is executed again.
As a simple example, define a generator that returns the numbers 1, 3, and 5 in turn:
def odd():
print('step 1') yield 1 print('step 2') yield(3) print('step 3') yield(5)
When calling the generator, first generate a generator object, and then use the next()
function to continuously get the next return value:
>>> o = odd()
>>> next(o)
step 1
1
>>> next(o)
step 2 3 >>> next(o) step 3 5 >>> next(o) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
It can be seen that odd
it is not an ordinary function, but a generator. During the execution process, it yield
will be interrupted when it encounters it, and the execution will continue next time. After executing 3 times yield
, there is no yield
more to execute, so the fourth call next(o)
will report an error.
Similarly, after changing the function to a generator, we basically never use it next()
to get the next return value, but directly use the for
loop to iterate:
>>> for n in fib(6):
... print(n) ... 1 1 2 3 5 8
But for
when the generator is called with a loop, it is found that the return value of the generator's return
statement cannot be obtained. If you want to get the return value, you must catch the StopIteration
error, and the return value is contained StopIteration
in value
:
>>> g = fib(6)
>>> while True: ... try: ... x = next(g) ... print('g:', x) ... except StopIteration as e: ... print('Generator return value:', e.value) ... break ... g: 1 g: 1 g: 2 g: 3 g: 5 g: 8 Generator return value: done
About how to catch errors, the error handling will be explained in detail later.