Unexpected PendingDeprecationWarning

Chris Angelico rosuav at gmail.com
Tue Nov 22 22:23:52 EST 2016


On Wed, Nov 23, 2016 at 1:50 PM, Nathan Ernst <nathan.ernst at gmail.com> wrote:
> I'm using Python 3.5.2, and the following code (when invoked) causes a
> PendingDeprecationWarning when used in a unit test:
>
> def identity(x):
>   return x
>
> def adjacent_difference(seq, selector=identity):
>   i = iter(seq)
>   l = selector(next(i))
>   while True:
>     r = selector(next(i))
>     yield r - l
>     l = r
>
> I wrote this to mimic the C++ std algorithm (defined here:
> http://en.cppreference.com/w/cpp/algorithm/adjacent_difference).
>
> What I don't understand is why I get this warning.
>
> The exact error message I get from unittest is:
> PendingDeprecationWarning: generator 'adjacent_difference' raised
> StopIteration
>
> I'd appreciate any insight into what is causing this deprecation warning,
> as I am stumped.

It's because there are some extremely confusing possibilities when a
generator raises StopIteration. In the example you post above, you're
probably expecting the behaviour you do indeed get, but there are
other ways of writing generators that would be a lot more surprising,
particularly when you refactor the generator a bit. The future
behaviour is much simpler to explain: *any* exception is an error, and
the way to cleanly terminate the generator is to return from it.

Here's how you'd write that generator for a post-3.6 world:

def adjacent_difference(seq, selector=identity):
  i = iter(seq)
  try: l = selector(next(i))
  except StopIteration: return
  for r in i:
    r = selector(r)
    yield r - l
    l = r

This is more explicit about the behaviour in the face of an empty
iterable (it will return without yielding any results), and uses the
most obvious way of stepping an iterator, namely a 'for' loop. You're
now iterating through 'i' and yielding stuff.

As a general rule, you should be able to replace 'yield x' with
'print(x)' and the code will do the same thing, only printing to the
console instead of being iterated over. Deliberately allowing
StopIteration to leak breaks that.

Here's the doc that set it all out:

https://www.python.org/dev/peps/pep-0479/

ChrisA



More information about the Python-list mailing list