In the previous learning process, we know that iterators have two benefits:
一是不依赖索引的统一的迭代方法二是惰性计算,节省内存
But Iterators also have their own significant drawbacks, which is
不如按照索引取值方便 一次性,只能向后取值,不能向前取值
So we also need to learn another object that has生成器
1. What is a generator
If a function body contains the yield keyword, the function is a generator function that executes the function to get a generator object
2. Get generator
First look at the following code
def foo(): print("first...") yield print("second...") yield print("third...")g=foo()print(g)
Based on the definition of the generator above: The 函数体内部包含yield关键字,则该函数就是生成器函数
result of the above function execution is a generator object
Execute the above code to see the results of the program execution
<generator object foo at 0x0000000001DF2BF8>
Can be seen:上面的函数执行的结果g就是一个生成器对象,上面的函数foo就是一个生成器函数
3. Built-in methods for generators
Modify the code above and call the Dir method to see the methods contained in the builder
def foo(): print("first...") yield print("second...") yield print("third...")g=foo()print(dir(g))
Print the method inside the generator, you can see the results of the printing
[‘__class__‘, ‘__del__‘, ‘__delattr__‘, ‘__dir__‘, ‘__doc__‘, ‘__eq__‘, ‘__format__‘, ‘__ge__‘, ‘__getattribute__‘, ‘__gt__‘, ‘__hash__‘, ‘__init__‘, ‘__init_subclass__‘, ‘__iter__‘, ‘__le__‘, ‘__lt__‘, ‘__name__‘, ‘__ne__‘, ‘__new__‘, ‘__next__‘, ‘__qualname__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__setattr__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘, ‘close‘, ‘gi_code‘, ‘gi_frame‘, ‘gi_running‘, ‘gi_yieldfrom‘, ‘send‘, ‘throw‘]
In these results, it can be seen __iter__方法
that there are and __next__方法
, thus can be judged生成器的本质就是迭代器
,生成器是迭代器的一种
4. Determine if the generator is an iterator
Modify the above code
def foo(): print("first...") yield print("second...") yield print("third...")g=foo()from collections import Iterableprint(isinstance(g,Iterable))
View Print Results
True
The above two examples can prove that:生成器的本质是迭代器,生成器就是迭代器的一种
5. The generator's
__iter__方法
And
__next__方法
Since the essence of the generator is an iterator, __iter__方法
__next__方法
What will be the result of invoking the generator's and
Modify the above code
def foo(): print("first...") yield print("second...") yield print("third...")g=foo()print(g)print(g.__iter__())g.__next__()
Program execution Results
<generator object foo at 0x0000000001DF2BF8><generator object foo at 0x0000000001DF2BF8>first...
From the above program execution results can be seen:直接打印生成器g和调用生成器g.__iter__方法,得到的结果都是生成器对象g 在内存中的地址
The calling method, in effect, takes a value out of the g.__next__
generator G, executes the g.__next__
method once, and triggers the generator's value operation, which is shown in the code above as the down-execution of the Foo function
As you can see from the execution results of the above program, only the first print function of the Foo function is executed, and the second and third print functions are not executed.
通常对函数来说,函数开始执行以后直到return语句,函数才会停止执行
Once the method is executed here g.__next__
, the Foo function executes a line of code, and the yield is stopped, where yield seems to play the role of return.
实际上,yield关键字的功能之一就是起到返回的作用
The above program execution encountered yield, this g.__next__
method is completed.
在函数的执行过程中,如果函数的return语句有返回值,则函数的执行完成就得到return语句的返回值,如果return没有定义返回值或者函数中没有定义return语句,则函数的执行结果默认为None
Modify the above code to print the __next__
execution result of the method
def foo(): print("first...") yield print("second...") yield print("third...")g=foo()print(g.__next__())
Program execution Results
first...None
As you can see, when the method is called, no __next__
arguments are received after yield,yield默认的返回值也是None
6.yield followed by return value
So what happens to the program execution if you take a return value after the yield keyword?
Modify the above code, followed by a return value after the yield keyword, to see the execution results of the program
def foo(): print("first...") yield 1 print("second...") yield 2 print("third...")g=foo()print(g.__next__())
Program execution Results
first...1
As can be seen from the execution results of the above program, yield will return the number of subsequent calls, as __next__
the result of the method execution
The difference between 7.yield and return
In a function, no matter how many return statements are defined in a function, the function is aborted when it executes to the first return statement, and the statements following it will not be executed again.
For yield, each time the method is called, the __next__
program executes from the start down until the yield statement is encountered, and the program pauses until the second call __next__
method, where the program resumes execution from the last pause until it encounters the next yield or the program executes
在上面的例子里,是使用yield把函数foo变成一个生成器,执行foo函数时,并不会立即执行foo函数,而是先得到生成器g,当调用一次`g.__next__`方法时,函数foo开始向下执行,遇到yield时,程序暂停,当下一次调用`g.__next__`方法时,函数foo继续从上一次暂停的地方开始向下执行,直到遇到yield暂停
8. Stopiteration of the generator
Modify the program, call the method multiple times __next__
to see the execution results of the program
def foo(): print("first...") yield print("second...") yield print("third...")g=foo()print(g)print(g.__iter__())g.__next__()print(‘*‘*30)g.__next__()print(‘#‘*30)g.__next__()
Program execution Results
<generator object foo at 0x0000000001DF2BF8><generator object foo at 0x0000000001DF2BF8>first...******************************second...##############################third...Traceback (most recent call last): File "E:/py_code/test.py", line 28, in <module> g.__next__()StopIteration
As you can see from the execution of the above program, each time a generator is called, __next__
a return value is obtained, which is equivalent to taking a value from the iterator.
If the program does not get the return value during execution, it means that the last value of the iterator has been traversed, so the method is called again __next__
, and the program throws an exception
9. For loop traversal of the generator
In the previous study already knew, 生成器本质上就是一个迭代器
. Since it is an iterator, of course, you can use the For loop to traverse the generator
Modify the example above and use the For loop to traverse the generator
def foo(): print("first...") yield 1 print("second...") yield 2 print("third...")g=foo()for i in g: print(i) print("*"*30)
To view the execution results of a program
first...1******************************second...2******************************third...
In the example above, each execution of a for loop is equivalent to executing the method once, and yield returns the number after which it was followed g.__next__
, so the first two execution results for the For loop are the print function and the number of yield followed
The For loop executes to the third time, executes the print function, the program throws StopIteration
an exception, but StopIteration
the exception is caught by the for loop, so the For loop executes the third time only the print statement is executed
10. Summary:
The yield keyword features:
与return的功能类似,都可以返回值,但不一样的地方在于一个函数中可以多次调用yield来返回值为函数封装好了`__iter__方法`和`__next__方法`,把函数的执行结果变成了迭代器`遵循迭代器的取值方式(obj.__next__())`,触发的函数的执行,函数暂停与再继续都由yield保存
11. Example: Using yield to simulate a command in Linux: Tail-f | grep ' ERROR ' | grep ' 404 '
The code is as follows:
import timedef tail(file_path, encoding=‘utf-8‘): with open(file_path, encoding=encoding) as f: f.seek(0, 2) while True: line = f.readline() if line: yield line else: time.sleep(0.5)def grep(lines, pattern): for line in lines: if pattern in line: yield lineg1 = tail(‘a.txt‘)g2 = grep(g1, ‘error‘)g3 = grep(g2, ‘404‘)for i in g3: print(i)
Python-Functional Programming builder