yield
表达式在Python的使用中非常广泛,最近在做爬虫项目的时候经常有用到。yield
在函数中的功能类似于return
,不同的是yield
每次返回结果之后函数并没有退出,而是每次遇到yield
关键字后返回相应结果,并保留函数当前的运行状态,等待下一次的调用。如果一个函数需要多次循环执行一个动作,并且每次执行的结果都是需要的,这种场景很适合使用yield
实现。
在Python文档中,对于yield表达式的描述为
yield
表达式仅在定义generator
函数时使用,因此只能用在函数定义的函数体中。在函数体中使用yield
表达式会使该函数成为generator
。
当调用
generator
函数时,它会返回一个iterator
,同时又是一个generator
。然后,generator
控制generator
函数的执行。当一个生成器的方法被调用时执行开始。此时,执行进行到第一个yield表达式,在那里它被再次挂起,将expression_list
的值返回给生成器的调用者。挂起,我们的意思是保留所有局部状态,包括局部变量的当前绑定,指令指针,内部计算栈和任何异常处理的状态。当通过调用其中一个生成器的方法来恢复执行时,函数可以像yield表达式只是另一个外部调用一样继续进行。恢复后的yield表达式的值取决于恢复执行的方法。如果使用__next__()
(通常通过for
或next()
内置函数),则结果为None
。否则,如果使用send()
,则结果将是传递到该方法的值。
其中提到了两个在Python比较重要的类型,generator
和iterator
,生成器和迭代器,在弄懂生成器之前,先来了解一下迭代器。
迭代器
迭代是Python最强大的功能之一,是访问集合元素的一种方式。
迭代器是一个可以记住遍历的位置的对象。
迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
迭代器有两个基本的方法:iter() 和 next()。
字符串,列表或元组对象都可用于创建迭代器:
1 | >>>list=[1,2,3,4] |
关于迭代器,我们只要了解最关键的地方在于它的__iter__()
与 __next__()
方法。
__iter__()
方法返回一个特殊的迭代器对象, 这个迭代器对象实现了__next__()
方法并通过 StopIteration 异常标识迭代的完成。
__next__()
方法(Python 2 里是 next())会返回下一个迭代器对象。
下面再来回到关于生成器,简单理解迭代器和生成器的关系其实是包含,生成器能做到的事,迭代器也能做到,生成器是一种可以简单有效的创建迭代器的工具。生成器在需要返回数据时使用yield
语句,每个yield
返回一个generator iterator
的函数,记住执行的位置和状态(包括局部变量和等待的 try 语句)。当generator iterator
恢复时,即对它调用next()时,它从离开的位置重新开始(与函数不同,函数每次调用从起始开始,并会记住所有的数据值和上次执行的语句)。
1 | def echo(value=None): |
more…
Specification: Try/Except/Finally
As noted earlier,
yield
is not allowed in thetry
clause of atry/finally
construct. A consequence is that generators should allocate critical resources with great care. There is no restriction onyield
otherwise appearing infinally
clauses,except
clauses, or in thetry
clause of atry/except
construct:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 > >>> def f():
> ... try:
> ... yield 1
> ... try:
> ... yield 2
> ... 1/0
> ... yield 3 # never get here
> ... except ZeroDivisionError:
> ... yield 4
> ... yield 5
> ... raise
> ... except:
> ... yield 6
> ... yield 7 # the "raise" above stops this
> ... except:
> ... yield 8
> ... yield 9
> ... try:
> ... x = 12
> ... finally:
> ... yield 10
> ... yield 11
> >>> print list(f())
> [1, 2, 4, 5, 8, 9, 10, 11]
>