Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/311.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 理解非平凡情况下生成器内部的StopIteration处理_Python_Python 3.x_Exception_Generator_Stopiteration - Fatal编程技术网

Python 理解非平凡情况下生成器内部的StopIteration处理

Python 理解非平凡情况下生成器内部的StopIteration处理,python,python-3.x,exception,generator,stopiteration,Python,Python 3.x,Exception,Generator,Stopiteration,我正在帮助维护一些现在包括自动Python 3.7测试的代码。这让我想到了一些与“生成器内部的更改迭代处理”相关的问题。我天真的理解是,可以使用try-except块修改旧代码,使之与所有python版本兼容,例如 旧代码: def f1(): it = iter([0]) while True: yield next(it) print(list(f1())) # [0] (in Py 3.6) # "RuntimeError: generator raise

我正在帮助维护一些现在包括自动Python 3.7测试的代码。这让我想到了一些与“生成器内部的更改迭代处理”相关的问题。我天真的理解是,可以使用try-except块修改旧代码,使之与所有python版本兼容,例如

旧代码:

def f1():
    it = iter([0])
    while True:
        yield next(it)

print(list(f1()))
# [0] (in Py 3.6)
# "RuntimeError: generator raised StopIteration" (in Py 3.7;
# or using from __future__ import generator_stop)
变成:

def f2():
    it = iter([0])
    while True:
        try:
            yield next(it)
        except StopIteration:
            return 

print(list(f2()))
# [0] (in all Python versions)
对于这个简单的例子,它是有效的,但我发现对于一些更复杂的代码,我正在重新分解它,但它不起作用。以下是Py 3.6的一个简单示例:

class A(list):
    it = iter([0])
    def __init__(self):
        while True:
            self.append(next(self.it))

class B(list):
    it = iter([0])
    def __init__(self):
        while True:
            try:
                self.append(next(self.it))
            except StopIteration:
                raise

class C(list):
    it = iter([0])
    def __init__(self):
        while True:
            try:
                self.append(next(self.it))
            except StopIteration:
                return  # or 'break'

def wrapper(MyClass):
    lst = MyClass()
    for item in lst:
        yield item

print(list(wrapper(A)))
# [] (wrong output)
print(list(wrapper(B)))
# [] (wrong output)
print(list(wrapper(C)))
# [0] (desired output)
我知道
A
B
示例完全相同,
C
案例是与Python3.7兼容的正确方式(我还知道将
重新分解为
for
循环对于许多示例都是有意义的,包括这个人为设计的示例)


但问题是,为什么带有
A
B
的示例会生成一个空列表
[]
,而不是
[0]

前两种情况在类的
\uuuu init\uuuu
中有一个未捕获的
停止迭代。在Python3.6中,
list
构造函数可以很好地处理这个问题(根据版本的不同,可能会有一个警告)。但是,异常会在
包装器
有机会迭代之前传播:实际失败的行是
lst=MyClass()
,并且lst:
中的项的循环
从未运行,导致生成器为空

在Python 3.6.4中运行此代码时,我在
print
行(对于
A
B
)上都收到以下警告:

这里的结论有两方面:

  • 不要让迭代器自行运行。你的工作是检查它何时停止。使用
    for
    循环很容易做到这一点,但必须使用
    while
    循环手动完成。案例
    A
    就是一个很好的例子
  • 不要重新引发内部异常。返回
    None
    。案例
    B
    不是一个好办法。
    中断
    返回
    将在
    块中正常工作,但
    块除外,就像您在
    C
    中所做的那样
  • 鉴于
    for
    循环是
    C
    中try-except块的语法糖,我通常建议使用它们,即使手动调用
    iter

    class D(list):
        it = iter([0])
        def __init__(self):
            for item in it:
                self.append(item)
    

    此版本在功能上等同于
    C
    ,并为您完成所有簿记工作。很少有情况需要实际的
    while
    循环(我会跳过对
    next
    的调用,但即使这些情况也可以用嵌套循环重写)。

    有趣的是,Python 3.6.6没有任何警告;但我认为你的解释基本上是正确的,谢谢!我想这就是为什么他们引入PEP来解释这些错误@克里斯·兰兹。这就是为什么即使手动调用
    iter
    我仍然喜欢
    for
    循环。它基本上就是选项
    C
    中try-catch块的语法糖。节省了大量的输入,而且考虑到流程的性质,您很少需要其他任何东西。在这种情况下,我的真实示例不像我展示的那样做作,但总体上我同意“列表包装器在Python 3.6中处理得很好”——很清楚,不是列表包装器处理它,对吗?这仅仅是因为
    yield
    将整个过程转化为生成器函数(但是您是正确的,生成器终止于
    lst=MyClass()
    行),是吗?(如果没有
    yield
    仍然会出现
    StopIteration
    问题)
    class D(list):
        it = iter([0])
        def __init__(self):
            for item in it:
                self.append(item)