A function that evaluates short-circuits and returns a value other than None that first appeared.

I thought about how to write a function that returns a value other than None that first appeared in short-circuit evaluation in Python.

First, as an example, execute the function get_answer () that evaluates three expressions, yahoo (), google (), and ʻask ()` in order, and returns a value other than None that appears first. I wrote get_answer.py normally. The operation has been confirmed with Python 2.7.3 and Python 3.2.3.

get_answer.py


#!/usr/bin/env python
# vim:fileencoding=utf-8

from __future__ import print_function

yahoo = lambda *_: print("in yahoo()") or None
google = lambda *_: print("in google()") or 0
ask = lambda *_: print("in ask()") or "draw"


def get_answer(question):
    answer = yahoo(question)
    if answer is None:
        answer = google(question)
        if answer is None:
            answer = ask("sagami", question)
    return answer

if __name__ == '__main__':  # pragma: nocover
    print("Answer is {}.".format(get_answer("1 - 1")))

When you do this,

in yahoo()
in google()
Answer is 0.

Is displayed. Evaluating google (question) gave a non-None value of 0, so ʻask ("sagami ", question)` was not evaluated. If you want to make the nesting of if statements shallow, the number of lines will increase slightly,

def get_answer(question):
    answer = yahoo(question)
    if answer is not None:
        return answer
    answer = google(question)
    if answer is not None:
        return answer
    return ask("sagami", question)

You can also write like this. But this function

def get_answer(question):
    return (yahoo(question) or
            google(question) or
            ask("sagami", question))

Or

def get_answer(question):
    for answer in (yahoo(question),
                   google(question),
                   ask("sagami", question)):
        if answer is not None:
            return answer
    return None

Cannot be written like. When the former is executed,

in yahoo()
in google()
in ask()
Answer is draw.

And 0, which is a value other than None, will be ignored, and when the latter is executed,

in yahoo()
in google()
in ask()
Answer is 0.

Therefore, even ʻask ("sagami", question) `, which does not need to be evaluated, will be evaluated.

As a result of various worries there, by using the decorator @anyone,

@anyone
def get_answer(question):
    yield yahoo(question)
    yield google(question)
    yield ask("sagami", question)

I came up with a way to write. The contents of @anyone are

def anyone(function):
    def _(*args, **kwargs):
        for value in function(*args, **kwargs):
            if value is not None:
                return value
        return None

    return _

is. I think that get_answer () can be written simply, but how about it? I would appreciate it if you could tell me if there is any other better way.

Below is get_answer.py rewritten using @ anyone.

get_answer.py


#!/usr/bin/env python
# vim:fileencoding=utf-8

from __future__ import print_function

yahoo = lambda *_: print("in yahoo()") or None
google = lambda *_: print("in google()") or 0
ask = lambda *_: print("in ask()") or "draw"


def anyone(function):
    def _(*args, **kwargs):
        for value in function(*args, **kwargs):
            if value is not None:
                return value
        return None

    return _


@anyone
def get_answer(question):
    yield yahoo(question)
    yield google(question)
    yield ask("sagami", question)

if __name__ == '__main__':  # pragma: nocover
    print("Answer is {}.".format(get_answer("1 - 1")))

Recommended Posts

A function that evaluates short-circuits and returns a value other than None that first appeared.
A function that returns a random name
# Function that returns the character code of a string
[C / C ++] Pass the value calculated in C / C ++ to a python function to execute the process, and use that value in C / C ++.