51414


とある関数Aは、引数に応じて文字列とリストのどちらかを返します。  
リストを返すときにはyieldし、文字列を返すときにはreturnで返したいです。  
ところが、文字列を返すときにもgeneratorが返り、使い物になりません。  
どうしたらこのような関数を作れますでしょうか。

(なお、print文で簡易デバッグをしたところ、文字列を返すときのreturn文は実行されているようです。)


CC BY-SA 4.0
2018-12-21T06:01:32.290
51410
2018-12-23T02:54:25.327
29212
question
python python3
yieldとreturnを併用したい


関数定義部分で `yield`
が使われている関数は[ジェネレータ](https://docs.python.org/ja/3/glossary.html#term-
generator)として扱われるため、通常の関数とは `return` の意味合いが変わります。たとえば次のプログラムが小さい動作例になります。

[code]

    def f(b):
        if b:
            yield "x"
            yield "y"
            yield "z"
        else:
            return "abc"
    
    if __name__ == '__main__':
        print(f(False))  # 文字列ではなく <generator object f> が返ってきます
    
[/code]

この挙動は [PEP 255](https://www.python.org/dev/peps/pep-0255/) や [PEP
380](https://www.python.org/dev/peps/pep-0380/) などで規定されています。要約すると「ジェネレータの中の
`return ほにゃらら` は `raise StopIteration(ほにゃらら)` のように扱うよ」と書かれています。したがって同じ関数の中で
`yield` と普通の `return` を同居させることはできません。

ただし、 **本当に**
場合によってジェネレータを返したりそうでないものを返したりしたいのであれば、関数内部でジェネレータを定義してそれが生成するオブジェクトを返すことによって実現はできます。あまり綺麗な実装には思えないので、設計がそれで良いのか考えてから使って下さい。

[code]

    def f(b):
        if b:
            def gen():
                yield "x"
                yield "y"
                yield "z"
            return gen()
        else:
            return "abc"
    
    if __name__ == '__main__':
        print(f(True))       # => <generator object f.<locals>.gen>
        print(list(f(True))) # => ['x', 'y', 'z']
        print(f(False))      # => abc
    
[/code]

上の例くらい簡単だと、[generator
expression](https://docs.python.org/ja/3/tutorial/classes.html#generator-
expressions) を使って書くこともできます。

[code]

    def f(b):
        if b:
            return (s for s in "xyz")
        else:
            return "abc"
    
[/code]

英語版 Stack Overflow での類似質問: [Return and yield in the same
function](https://stackoverflow.com/q/26595895/5989200)


CC BY-SA 4.0
2018-12-21T07:28:02.123
51414
2018-12-23T02:54:25.327
2018-12-23T02:54:25.327
19110
19110
51410
answer