一、Python yield内部工作机制解析
Python yield 是generator实现各种功能的核心。我们看一下yield是如何工作的,当调用generator的特定方法,譬如next(),其内部代码会执行到yield,通过下面这个具体代码示例来进行说明:
在while循环体中,使用关键字yield定义了一条语句:yield num,表示会返回一个值给调用者,而不是像return语句那样,返回值之后就会退出整个方法的执行。使用yield时,这个方法的状态(state)是被记忆的,也就是说,当调用一个generator对象的next()方法时,变量num先递增,然后使用yield返回新的值给调用者。
当执行Python中的yield语句时,程序会暂停(suspend)方法的执行(这里的“方法”其实可以看做是一个generator对象,所谓“暂停”,是指程序执行到yield语句时,不会再继续执行下面的语句),而是使用yield返回当前变量的值给调用者。当这个方法被暂停执行后,方法的状态会被保存下来,在状态中包括了任何绑定到这个generator对象的本地变量,代码调用指针,内部栈,以及任何的异常处理(异常也被保存到这个“方法”对象的状态中,你可以根据具体需要进行相应的处理)。
基于这样的工作机制,当你调用一个generator对象的方法时,就允许你继续执行之前被“暂停”的程序(从suspend到resume的过程),即继续执行yield语句之后的代码。
二、Python yield应用解析
下面是应用代码示例,在这个方法(这个方法可看做是一个generator对象)中定义了两个yield语句:
执行以下语句:
输出结果如下,从结果看,在对这个generator对象”multi_obj”调用next方法时,显然只会打印出这条信息:
This will print the first string
接下来继续执行下面的语句:
print(next(multi_obj))
输出结果如下,可以看到打印了第2条信息:
如果再继续执行下面的语句,我们看下会发生什么:
print(next(multi_obj))
因为在上面的方法中只定义了两条yield语句,所以当第3次执行print(next(multi_obj))时,会因为超出了迭代范围而导致抛出异常。从这个例子可以看到,当调用next方法时,程序执行到yield语句时就会停止,再次调用next方法后会继续从第一个yield语句后的语句开始执行,然后再次遇到yield语句又会停止。这个方法中的两条yield语句是顺序执行的,当然也可以把yield语句放在循环体中执行。
StopIteration是一个原生的异常,抛出表明迭代到达了一个iterator对象的结尾,你可以根据需要对异常进行处理。对于循环(loop)来说,可以基于StopIteration来构建,你可以使用while来实现自己的循环代码,譬如下面的代码示例,通过内部的yield工作机制来获取列表中定义的每个元素,当迭代结束时,可以使用break语句来让程序不抛出异常:
输出结果如下:
三、总结
Yield涉及到程序的”suspend”和”resume”,在两者之间存在一个状态(state)的管理,这构建了Python在数据处理领域的核心基础。这里主要是从generator的视角来谈yield,在很多高级编程代码中会看到直接使用yield的场景。Yield也为Python中的coroutine和消息循环的编程打下了基础。