ToolmakerSteve
ToolmakerSteve

Reputation: 21243

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

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?

Upvotes: 0

Views: 68

Answers (1)

DSM
DSM

Reputation: 353119

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]

Upvotes: 1

Related Questions