-1

EDIT: this is a duplicate of How to properly ignore exceptions.

My code:

try:
    y = next(x)
    z = next(y)
    f(y, z)
except StopIteration:
    pass

What I'd like to write instead:

with x as y, y as z:
    f(y, z)

However, that raises AttributeError: __enter__.

If with is not the appropriate idiom, is there a different idiomatic way to do what I'm doing without the explicit except StopIteration: pass, or is there a way to wrap an iterator in a type that supports with? The semantics should be exactly the same as in the first snippet.

I'm asking because for y in x will not work in this context.

Jordan
  • 4,510
  • 7
  • 34
  • 42
  • 5
    The purpose of `with` is to automatically clean up a resource when the block is exited. What does that have to do with iterators? – Barmar May 21 '20 at 01:19
  • 2
    It's incredibly difficult to imagine the context that makes any of this code make sense, nor that would explain why you can't use `for` with an iterator. It would probably help to see how `x` 's type is defined, and what problem you are trying to solve via `f`. – Karl Knechtel May 21 '20 at 01:22
  • As the question specifically asks, if `with` is not the appropriate idiom for ignoring `StopIteration` , is there a **different** idiom that's appropriate? – Jordan May 21 '20 at 01:23
  • 1
    What is your criteria for rejecting the explicit ``try`` syntax? Are you looking for *shorter* code? Is your goal to not protect ``f`` against ``StopIteration`` (which the ``try:`` does but not the ``with``)? Is there some reason why ``x`` has to be unwrapped like this, and could it possibly be circumvented? – MisterMiyagi May 21 '20 at 08:46
  • It's not that complicated. It just asks for a way to catch and ignore `StopIteration` without the explicit `except StopIteration`. The motive is exactly as people answered on [Why use contextlib.suppress?](https://stackoverflow.com/q/34566806/972499). A perfectly valid and informative answer would be "there's no feature for specifically ignoring `StopIteration`, but here's a feature for ignoring any exceptions you specify", and that's exactly what the accepted answer does. – Jordan May 21 '20 at 19:32

2 Answers2

2

If you want to retrieve one item from an iterator, the way to do that is next. There's no separate idiom for it; next is the dedicated "get one thing from an iterator" function. with definitely isn't it; with is for resource cleanup.

Your code is bizarre enough that it's hard to imagine a dedicated syntax ever being added for it.

If you really want to write it differently, you could do something like

for y in x:
    for z in y:
        f(y, z)
        break
    break

or

from itertools import islice

for y in islice(x, 1):
    for z in islice(y, 1):
        f(y, z)
user2357112
  • 260,549
  • 28
  • 431
  • 505
1

Use contextlib.suppress(*exceptions) to shorten try:-except (*exceptions,): pass constructs:

from contextlib import suppress

with suppress(StopIteration):
    y = next(x)
    f(y, next(y))

A syntax such as with helper(x) as y, helper(y) as z: is not possible: A context manager must return a value or raise an Exception. This makes it impossible to gracefully handle the case when next(x) or next(y) do not return a value.

MisterMiyagi
  • 44,374
  • 10
  • 104
  • 119