python循环捕捉异常_关于python:yield如何捕获StopIteration异常?

示例函数终止的原因:

def func(iterable):

while True:

val = next(iterable)

yield val

但如果我去掉yield语句函数,会引发stopIteration异常吗?

编辑:很抱歉误导你们。我知道发电机是什么以及如何使用它们。当然,当我说函数终止时,我并不是说要对函数进行急切的评估。我只是暗示,当我使用函数生成生成器时:

gen = func(iterable)

在func的情况下,它工作并返回相同的生成器,但在func2的情况下:

def func2(iterable):

while True:

val = next(iterable)

它将引发StopIteration,而不是无返回或无限循环。

让我更具体一点。itertools中有一个函数tee,相当于:

def tee(iterable, n=2):

it = iter(iterable)

deques = [collections.deque() for i in range(n)]

def gen(mydeque):

while True:

if not mydeque:             # when the local deque is empty

newval = next(it)       # fetch a new value and

for d in deques:        # load it to all the deques

d.append(newval)

yield mydeque.popleft()

return tuple(gen(d) for d in deques)

实际上,有一些魔力,因为嵌套函数gen具有无限的不带break语句的循环。当函数中没有任何项时,由于StopIteration异常,Gen函数将终止。但它正确地终止(不引发异常),即只停止循环。所以问题是:StopIteration在哪里处理?

你怎么叫这个?

要回答您的问题,关于StopIteration在itertools.tee内部创建的gen生成器中被捕获的位置:不是的。由tee结果的使用者在迭代时捕获异常。好的。

首先,需要注意的是,生成器函数(在任何地方都是带有yield语句的任何函数)与正常函数有根本不同。而不是在调用函数时运行函数的代码,而是在调用函数时得到一个generator对象。只有当您在生成器上迭代时,才会运行代码。好的。

生成器函数不会在不提升StopIteration的情况下完成迭代(除非它会引发其他异常)。StopIteration是发电机发出的信号,它是完成的,不是可选的。如果在不引发任何内容的情况下到达return语句或生成器函数代码的末尾,python将为您引发StopIteration!好的。

这与常规函数不同,后者返回None,前提是它们到达末尾时没有返回任何其他内容。它与发电机的不同工作方式有关,正如我上面所描述的。好的。

下面是一个示例生成器函数,可以很容易地看到StopIteration是如何被提升的:好的。

def simple_generator():

yield"foo"

yield"bar"

# StopIteration will be raised here automatically

以下是当你消费它时会发生的事情:好的。

>>> g = simple_generator()

>>> next(g)

'foo'

>>> next(g)

'bar'

>>> next(g)

Traceback (most recent call last):

File"", line 1, in

next(g)

StopIteration

调用simple_generator总是立即返回generator对象(不运行函数中的任何代码)。对generator对象的每个next调用都会运行代码,直到下一条yield语句,并返回生成的值。如果没有更多的数据,则增加StopIteration。好的。

现在,通常您不会看到StopIteration例外。这样做的原因是,您通常使用for循环内的生成器。for语句将自动反复调用next,直到StopIteration出现为止。它会捕捉并抑制你的StopIteration例外,所以你不需要为了处理它而与try或except块纠缠不清。好的。

像for item in iterable: do_suff(item)这样的for循环几乎完全等同于这个while循环(唯一的区别是真正的for不需要临时变量来保存迭代器):好的。

iterator = iter(iterable)

try:

while True:

item = next(iterator)

do_stuff(item)

except StopIteration:

pass

finally:

del iterator

您在顶部显示的gen生成器函数是一个例外。它使用迭代器产生的StopIteration异常,因为它使用自己的信号来完成对它的迭代。也就是说,它不是捕获StopIteration,然后跳出循环,而是让异常不被捕获(可能被更高级别的代码捕获)。好的。

与主要问题无关,还有一件事我想指出。在代码中,您在一个名为iterable的变量上调用next。如果您将这个名称作为文档来说明您将得到什么类型的对象,那么这不一定是安全的。好的。

next是iterator协议的一部分,而不是iterable协议(或container协议)。它可能适用于某些类型的iterables(如文件和生成器,因为这些类型是它们自己的迭代器),但对于其他iterables(如元组和列表),它将失败。更正确的方法是在你的iterable值上调用iter,然后在你收到的迭代器上调用next。(或者只使用for循环,在适当的时候为您调用iter和next)好的。

编辑:我刚刚在谷歌搜索相关问题时找到了自己的答案,我想我应该更新一下,指出上面的答案在将来的Python版本中不会完全正确。PEP479使得允许StopIteration在未从生成器函数捕获的情况下冒泡成为一个错误。如果发生这种情况,python将把它变成一个RuntimeError异常。好的。

这意味着需要修改类似于itertools中使用StopIteration中断生成器函数的示例的代码。通常,您需要使用try或except捕获异常,然后执行return。好的。

因为这是一个向后不兼容的变化,它正逐渐被逐步采用。在python 3.5中,默认情况下,所有代码都将像以前一样工作,但是您可以使用from __future__ import generator_stop获得新的行为。在Python3.6中,代码仍然可以工作,但它会给出警告。在Python3.7中,新的行为将一直适用。好的。好啊。

那么,stopIteration是由函数定义(或者等价的生成器结构)消耗的?我只想知道如果我们使用函数体的下一个外部,它将引发异常,但是如果我们使用内部函数,它将正常终止。

@Branalgue不,函数定义不会使用异常。与任何其他异常一样,StopIteration将进入调用堆栈,直到它被显式try/catch块捕获,或被for循环中的隐式块捕获。我认为您缺少的是StopIteration在生成器函数中不是问题。当你没有什么可屈服的时候,你就应该提高一个。您可以显式地使用raise StopIteration(),或者隐式地使用函数的末尾,或者让调用next生成的StopIteration不被捕获。

我明白。我不明白为什么StopIteration在生成器功能中不是问题。Generator隐式处理异常的声明是否正确?

@Branalgue:这不是问题,因为StopIteration是一个信号发生器用来显示它已经完成了。如果您在一个生成器函数中,并且使用next手动迭代迭代器,那么通常在迭代器用完时完成。所以,你不必提高你自己的StopIteration例外,你只需让next所提高的那一个冒泡。有一些反例,您希望生成最终值,或者需要在结束前进行一些特殊清理,在这些情况下,您需要捕获StopIteration。但这并不常见。

好啊。谢谢,@blckknght先生。

当函数包含yield时,调用它实际上不执行任何操作,它只创建一个生成器对象。只有对该对象进行迭代才能执行代码。所以我猜你只是在调用函数,这意味着函数不会提升StopIteration,因为它永远不会被执行。

考虑到你的功能和一个不可替代的:

def func(iterable):

while True:

val = next(iterable)

yield val

iterable = iter([1, 2, 3])

这是错误的称呼:

func(iterable)

这是正确的方法:

for item in func(iterable):

# do something with item

您还可以将生成器存储在变量中,并对其调用next()(或以其他方式对其进行迭代):

gen = func(iterable)

print(next(gen))   # prints 1

print(next(gen))   # prints 2

print(next(gen))   # prints 3

print(next(gen))   # StopIteration

顺便说一下,编写函数的更好方法如下:

def func(iterable):

for item in iterable:

yield item

或者在python 3.3及更高版本中:

def func(iterable):

yield from iter(iterable)

当然,真正的发电机很少如此微不足道。:-)

小心最后一个例子。list没有next或__next__方法,您的示例不起作用。不过,修复起来很容易。gen = func(iter([1,2,3]))

是的,刚刚意识到,修复了。-)

如果没有yield,您将迭代整个iterable,而不停止对val执行任何操作。while循环不捕获StopIteration异常。等效的for循环为:

def func(iterable):

for val in iterable:

pass

它确实捕获了StopIteration,并简单地退出循环,从而从函数返回。

您可以显式捕获异常:

def func(iterable):

while True:

try:

val = next(iterable)

except StopIteration:

break

yield没有抓到StopIteration。yield对函数的作用是使它成为一个生成器函数,而不是一个正则函数。因此,从函数调用返回的对象是一个可Iterable对象(当您使用next函数(由for循环隐式调用)请求它时,它计算下一个值)。如果您不使用yield语句,那么python将立即执行整个while循环,这最终耗尽了iterable(如果它是有限的),并在调用时立即引发StopIteration。

考虑:

x = func(x for x in [])

next(x)  #raises StopIteration

一个for循环捕捉到异常——这就是它知道什么时候停止在你给它的iterable上调用next的方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值