Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/rest/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 收益率如何捕获迭代异常?_Python_Yield_Stopiteration - Fatal编程技术网

Python 收益率如何捕获迭代异常?

Python 收益率如何捕获迭代异常?,python,yield,stopiteration,Python,Yield,Stopiteration,示例中函数终止的原因: def func(iterable): while True: val = next(iterable) yield val 但若我去掉yield语句函数会引发StopIteration异常吗 编辑:很抱歉误导了你们。我知道什么是发电机以及如何使用它们。当然,当我说函数终止时,我并不是指对函数的求值。我只是暗示,当我使用函数生成生成器时: gen = func(iterable) 对于func,它工作并返回相同的生成器,但对于f

示例中函数终止的原因:

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,而不是None-return或无限循环

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

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语句。当gen函数中没有项时,由于StopIteration异常而终止。但它正确地终止(不引发异常),即只是停止循环所以问题是:StopIteration在哪里处理?

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

def func(iterable):
    for val in iterable:
        pass
它捕获
StopIteration
,并简单地退出循环,从而从函数返回

您可以显式捕获异常:

def func(iterable):
    while True:
        try:
            val = next(iterable)
        except StopIteration:
            break

当一个函数包含
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()
(或以其他方式对其进行迭代):

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

def func(iterable):
    for item in iterable:
        yield item
或者在Python 3.3及更高版本中:

def func(iterable):
    yield from iter(iterable)

当然,真正的生成器很少如此微不足道。:-)

yield
没有捕捉到
停止迭代
yield
对您的函数所做的是,它使函数成为生成器函数而不是常规函数。因此,从函数调用返回的对象是一个iterable对象(当您使用
next
函数(由for循环隐式调用)请求它时,它计算下一个值)。如果不使用
yield
语句,那么python会立即执行整个
while
循环,最终耗尽iterable(如果它是有限的),并在调用它时正确地调用
StopIteration

考虑:

x = func(x for x in [])
next(x)  #raises StopIteration

for
循环捕获异常——这就是它如何知道何时停止对您提供的iterable调用
next

注意:这个问题(以及我对它的回答的原始部分)只对3.7之前的Python版本真正有意义。由于中所述的更改,3.7及更高版本中不再出现所询问的行为。所以这个问题和原始答案只在历史文物中有用。在PEP被接受后,我在答案的底部添加了一个与Python的现代版本更相关的部分。


要回答您关于在
itertools.tee
内部创建的
gen
生成器中捕获
StopIteration
的问题,请执行以下操作:它没有。由
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 "<pyshell#6>", line 1, in <module>
    next(g)
StopIteration
顶部显示的发电机功能是一个例外。它使用它正在使用的迭代器生成的
StopIteration
异常作为它自己的信号,表示它已经完成了迭代。也就是说,它不是捕获
StopIteration
,然后跳出循环,而是简单地让异常未捕获(可能被某个更高级别的代码捕获)

与主要问题无关,我想指出另一件事。在代码中,对名为
iterable
的变量调用
next
。如果您将该名称作为您将获得的对象类型的文档,那么这并不一定是安全的

next
iterator
协议的一部分,而不是
iterable
(或
container
)协议。它可能适用于某些类型的iterables(例如文件和生成器,因为这些类型是它们自己的迭代器),但对于其他类型的iterables,它将失败
>>> g = simple_generator()
>>> next(g)
'foo'
>>> next(g)
'bar'
>>> next(g)
Traceback (most recent call last):
  File "<pyshell#6>", line 1, in <module>
    next(g)
StopIteration
iterator = iter(iterable)
try:
    while True:
        item = next(iterator)
        do_stuff(item)
except StopIteration:
    pass
finally:
    del iterator
def split_to_chunk(size: int, iterable: Iterable) -> Iterable[Iterable]:
    source_iter = iter(iterable)
    while True:
        batch_iter = itertools.islice(source_iter, size)
        try:
            yield itertools.chain([next(batch_iter)], batch_iter)
        except StopIteration:
            return
def sample_gen() -> Iterable[int]:
    i = 0
    while True:
        yield i
        i += 1

for chunk in split_to_chunk(7, sample_gen()):
    pprint.pprint(list(chunk))
    time.sleep(2)
[0, 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, 26, 27]
............................