「Python で複数の値を返す」ことについて、いくつかの議論がありました (例: 1、 2 ) 。これは、ここで見つけようとしている「複数の値を返す」パターンではありません。何を使用しても (タプル、リスト、辞書、オブジェクト)、それは単一の戻り値であり、その戻り値 (構造) を何らかの方法で解析する必要があります。
複数の戻り値の真の利点は、アップグレード プロセスにあります。例えば、
もともと、あなたは
def func():
return 1
print func() + func()
次にfunc()
、追加の情報を返すことができるが、以前のコードを壊したくない (またはコードを 1 つずつ変更したくない) と判断しました。のように見えます
def func():
return 1, "extra info"
value, extra = func()
print value # 1 (expected)
print extra # extra info (expected)
print func() + func() # (1, 'extra info', 1, 'extra info') (not expected, we want the previous behaviour, i.e. 2)
以前のコード ( func() + func()
) は壊れています。あなたはそれを修正する必要があります。
質問を明確にしたかどうかはわかりません... CLISPの例を見ることができます。このパターンを Python で実装する同等の方法はありますか?
編集: 上記のclisp スニペットをオンラインに置いて、簡単に参照できるようにします。
ここで、複数の戻り値パターンの2 つの使用例を挙げましょう。おそらく、誰かが2つのケースに対する代替ソリューションを持っている可能性があります:
- スムーズなアップグレードをサポートします。これは上記の例に示されています。
- クライアント側のコードをよりシンプルにします。私がこれまでに持っている次の代替ソリューションを参照してください。例外を使用すると、アップグレード プロセスがスムーズになりますが、コードのコストが高くなります。
現在の代替案: (それらは「複数の値を返す」構造ではありませんが、上記のポイントのいくつかを満たすエンジニアリング ソリューションになる可能性があります)
- タプル、リスト、辞書、オブジェクト。言われているように、クライアント側からの特定の解析が必要です。例えば
if ret.success == True: blabla
。ret = func()
その前に必要です。書く方がはるかにきれいif func() == True: blabal
です。 - を使用し
Exception
ます。このスレッドで説明されているように、「False」のケースがまれな場合、それは良い解決策です。この場合でも、クライアント側のコードはまだ重すぎます。 - などの引数を使用し
def func(main_arg, detail=[])
ます。は、デザインに応じてオブジェクトにすることも、オブジェクトにすることもdetail
できます。は元の単純な値のみを返します。詳細は引数に移動します。問題は、クライアントが詳細を保持するために、呼び出しの前に変数を作成する必要があることです。list
dict
func()
detail
- 「詳細」インジケータを使用し
def func(main_arg, verbose=False)
ます。verbose == False
(デフォルト; クライアントの使用方法) の場合func()
、元の単純な値を返します。の場合verbose == True
、単純な値と詳細を含むオブジェクトを返します。 - 「バージョン」インジケータを使用します。「verbose」と同じですが、そこにアイデアを拡張します。このようにして、返されたオブジェクトを複数回アップグレードできます。
- グローバルを使用し
detail_msg
ます。これは古い C スタイルのようなものerror_msg
です。このようにして、関数は常に単純な値を返すことができます。クライアント側はdetail_msg
必要に応じて参照できます。detail_msg
ユース ケースに応じて、グローバル スコープ、クラス スコープ、またはオブジェクト スコープに配置できます。 - ジェネレーターを使用します。
yield simple_return
そしてyield detailed_return
。この解決策は、呼び出し先側に適しています。ただし、呼び出し元はfunc().next()
andのようなことをしなければなりませんfunc().next().next()
。オブジェクトでラップし、 をオーバーライドし__call__
て少し単純化できます (例:func()()
)。ただし、呼び出し側から見ると不自然に見えます。 - 戻り値にはラッパー クラスを使用します。クラスのメソッドをオーバーライドして、元の単純な戻り値の動作を模倣します。詳細なデータをクラスに入れます。私たちのプロジェクトでは、
bool
戻り値の型を扱う際にこの代替手段を採用しました。関連するコミットを参照してください:https://github.com/fqj1994/snsapi/commit/589f0097912782ca670568fe027830f21ed1f6fc
(投稿にリンクを追加するのに十分な評判がありません... ---//)
ここにいくつかの解決策があります:
- @yupbank の回答に基づいて、デコレーターに形式化しました。
github.com/hupili/multiret
- 上記の 8 番目の選択肢は、クラスをラップできることを示しています。これが、私たちが採用した現在のエンジニアリング ソリューションです。より複雑な戻り値をラップするために、メタ クラスを使用して必要なラッパー クラスをオンデマンドで生成することができます。試したことはありませんが、これは堅牢なソリューションのように思えます。