python 2.7 - is there a more succint way to do this series of yield statements (in python 3, "yield from" would help)

StackOverflow https://stackoverflow.com/questions/20675752

  •  19-09-2022
  •  | 
  •  

Question

Situation:

Python 2.7 code that contains a number of "yield" statements. But the specs have changed. Each yield calls a function that used to always return a value. Now the result is sometimes a value that should be yielded, but sometimes no value should be yielded.

Dumb Example:

BEFORE:

def always(x):
    return 11 * x

def do_stuff():
    # ... other code; each yield is buried inside an if or other flow construct ...
    # ...
    yield always(1)
    # ...
    yield always(6)
    # ...
    yield always(5)

print( list( do_stuff() ) )

=>

[11, 66, 55]

AFTER (if I could use Python 3, but that is not currently an option):

def maybe(x):
    """ only keep odd value; returns list with 0 or 1 elements. """
    result = 11 * x
    return [result] if bool(result & 1) else []

def do_stuff():
    # ...
    yield from maybe(1)
    # ...
    yield from maybe(6)
    # ...
    yield from maybe(5)

=>

[11, 55]

AFTER (in Python 2.7):

def maybe(x):
    """ only keep odd value; returns list with 0 or 1 elements. """
    result = 11 * x
    return [result] if bool(result & 1) else []

def do_stuff():
    # ...
    for x in maybe(1): yield x
    # ...
    for x in maybe(6): yield x
    # ...
    for x in maybe(5): yield x

NOTE: In the actual code I am translating, the "yields" are buried inside various flow-control constructs. And the "maybe" function has two parameters, and is more complex.


MY QUESTION:

Observe that each call to "maybe" returns either 1 value to yield, or 0 values to yield. (It would be fine to change "maybe" to return the value, or to return None when there is no value, if that helps.)

Given this 0/1 situation, is there any more succinct way to code?

Was it helpful?

Solution

If as you say you can get away with returning None, then I'd leave the code as it was in the first place:

def maybe(x):
    """ only keep odd value; returns either element or None """
    result = 11 * x
    if result & 1: return result

def do_stuff():
    yield maybe(1)
    yield maybe(6)
    yield maybe(5)

but use a wrapped version instead which tosses the Nones, like:

def do_stuff_use():
    return (x for x in do_stuff() if x is not None)

You could even wrap the whole thing up in a decorator, if you wanted:

import functools

def yield_not_None(f):
    @functools.wraps(f)
    def wrapper(*args, **kwargs):
        return (x for x in f(*args, **kwargs) if x is not None)
    return wrapper

@yield_not_None
def do_stuff():
    yield maybe(1)
    yield maybe(6)
    yield maybe(5)

after which

>>> list(do_stuff())
[11, 55]
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top