1
1

More than 5 years have passed since last update.

短絡評価して最初に現れたNone以外の値を返す関数

Last updated at Posted at 2012-07-01

Pythonで、短絡評価して最初に現れたNone以外の値を返す関数の書き方について考えてみました。

まずは例として3つの式、yahoo()google()、そしてask()を順番に評価し、最初に現れたNone以外の値を返す関数get_answer()を実行する get_answer.py を普通に書いてみました。動作はPython 2.7.3と 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")))

これを実行すると、

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

のように表示されます。google(question)を評価してNone以外の値である0が得られましたので、ask("sagami", question)は評価されていません。
if文のネストを浅くしたいのであれば、行数は若干増えますが、

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)

のように書くこともできます。しかし、この関数を

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

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

のように書くことはできません。前者を実行すると、

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

となって、None以外の値である0が無視されてしまいますし、後者を実行すると、

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

となって、評価する必要がないask("sagami", question)まで評価されてしまいます。

そこで色々悩んだ結果、デコレータ@anyoneを使うことで、

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

と書く方法を思いつきました。@anyoneの中身は、

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

    return _

です。get_answer()はシンプルに書けるようになったと思うのですがいかがでしょうか?
他に何かもっと良い方法があれば、教えていただけると助かります。

以下は、@anyoneを使って書き直した get_answer.py です。

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")))
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1