我正在使用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
答案 0 :(得分:2)
这是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
的条件是真实的,生成器将在产生两个值(不产生3
或4
)后停止。但是,如果你试图重构函数的中间部分怎么办?如果您将零件从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
吃了辅助生成器函数中引发的StopIteration
。它假设只应停止辅助发生器,因此外部发电机继续运行。
为了解决这个问题,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
例外的地方,并使用try
和except
逻辑对其进行包装。对于您问题中的代码:
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))
答案 1 :(得分:1)
抛出弃用警告是因为Python语言中即将发生的非向后兼容更改将从版本3.7开始激活,并记录在PEP-479中。
最重要的部分:
摘要
此PEP建议更改生成器:引发
RuntimeError
时 在生成器内部,它被替换为__future__
。 (更多 确切地说,当异常即将消失时,就会发生这种情况 发电机的堆栈框架。)因为变化是倒退的 不兼容,该功能最初是使用__future__
引入的 言。
和
过渡计划
Python 3.5:在
StopIteration
下启用新语义 进口;如果__future__
气泡出现,则静默弃用警告 生成器不在post "/weather" do puts HTTP.get('https://www.metaweather.com/api/location/search/?query=milwaukee') # prints correct result HTTP.get('https://www.metaweather.com/api/location/search/?query=milwaukee').to_s end
导入下。 Python 3.6:非静默弃用警告。 Python 3.7:在任何地方启用新语义。
您要关联的文档应该更新。
答案 2 :(得分:0)
将来不推荐使用模块时会抛出弃用警告。现在,您可以使用它。只需在代码中包含以下内容即可看到这些警告:
import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)
warnings.simplefilter(action='ignore', category=DeprecationWarning)