繁体   English   中英

使用IPython时生成器中出现意外的弃用警告

[英]Unexpected DeprecationWarning in generator when using IPython

我正在使用python docs提供的修改后的grouper配方:

from itertools import chain, islice
def grouper(iterable, n):
    iterable = iter(iterable)
    while True:
        peek = next(iterable)
        yield chain((peek,), islice(iterable, n - 1))

这似乎工作正常。 我可以这样做:

>>> x = bytearray(range(16))
>>> [int.from_bytes(n, 'big') for n in grouper(x, 4)]
[66051, 67438087, 134810123, 202182159]

但是,当我在IPython中运行完全相同的代码时,我得到了一个DeprecationWarning

In [1]: from itertools import chain, islice
   ...: def grouper(iterable, n):
   ...:      iterable = iter(iterable)
   ...:      while True:
   ...:          peek = next(iterable)
   ...:          yield chain((peek,), islice(iterable, n - 1))

In [2]: x = bytearray(range(16))

In [3]: [int.from_bytes(n, 'big') for n in grouper(x, 4)]
__main__:1: DeprecationWarning: generator 'grouper' raised StopIteration
Out[3]: [66051, 67438087, 134810123, 202182159]

警告来自何处以及为什么我在常规Python控制台中看不到它? 我该怎么办才能使警告消失?

我使用的是Python 3.6.2和IPython 6.1.0

这是Python的一个变化,正逐步在Python 3.5和Python 3.7之间分阶段进行。 细节在PEP 479中有解释,但我会尝试快速概述。

问题是从生成器函数泄漏的StopIteration异常。 看起来似乎没问题,因为提高StopIteration是迭代器完成的信号。 但它可能会导致重构生成器的问题。 这是一个显示问题的示例:

假设您有这个生成器函数(在3.5之前的Python版本中可以正常工作,它开始发出警告):

def gen():
    yield 1
    yield 2
    if True:
        raise StopIteration
    yield 3
    yield 4

由于if的条件是真实的,因此在产生两个值(不产生34 )之后,发电机将停止。 但是,如果你试图重构函数的中间部分怎么办? 如果将零件从yield 2移动到yield 3生成辅助生成器,您将看到一个问题:

def gen():
    yield 1
    yield from refactored_helper()
    yield 4

def refactored_helper():
    yield 2
    if True:
        raise StopIteration
    yield 3

在这个版本中,将跳过3 ,但仍然会产生4 那是因为yield from Helper生成器函数中引发的StopIterationyield from 它假设只应停止辅助发生器,因此外部发电机继续运行。

为了解决这个问题,Python开发人员决定改变生成器的工作方式。 从Python 3.7开始,解释器会将一个从生成器函数泄漏的StopIteration异常更改为RuntimeError异常。 如果要正常退出生成器,则需要使用return 此外,您现在可以从生成器函数return值。 该值将包含在由生成器机制引发的StopIteration异常中,并且表达式的yield from将计算为返回值。

所以上面的生成器可以适当地重构为:

def gen():
    yield 1
    if yield from refactored_helper():
        return
    yield 4

def refactored_helper():
    yield 2
    if True:
        return True
    yield 3
    # like a normal function, a generator returns None if it reaches the end of the code

如果您现在要编写与未来兼容的代码,则应将from __future__ import generator_stop放在模块的顶部。 然后你需要跟踪你正在泄漏StopIteration异常的地方,并用tryexcept逻辑包装它们。 对于您问题中的代码:

from __future__ import generator_stop

from itertools import chain, islice

def grouper(iterable, n):
    iterable = iter(iterable)
    while True:
        try:
            peek = next(iterable)
        except StopIteration:
            return
        yield chain((peek,), islice(iterable, n - 1))

引发弃用警告是因为Python语言中即将发生的非向后兼容更改将从版本3.7开始激活,并在PEP-479中进行了记录

最重要的部分:

抽象

此PEP建议对生成器进行更改:当在生成器内引发StopIteration时,将其替换为RuntimeError (更确切地说,当异常即将从生成器的堆栈帧中冒出时,就会发生这种情况。)由于更改是向后不兼容的,因此最初使用__future__语句引入该功能。

过渡计划

Python 3.5:在__future__ import下启用新语义; 如果StopIteration冒出不在__future__导入下的生成器,则静默弃用警告。 Python 3.6:非静默弃用警告。 Python 3.7:在任何地方启用新语义。

您要链接的文档应该更新。

如果将来不推荐使用模块,则抛出弃用警告。现在,您可以使用它。 只需在代码中包含以下内容即可看到这些警告:

import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)
warnings.simplefilter(action='ignore', category=DeprecationWarning)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM