0

「Python で複数の値を返す」ことについて、いくつかの議論がありました (例: 12 ) 。これは、ここで見つけようとしている「複数の値を返す」パターンではありません。何を使用しても (タプル、リスト、辞書、オブジェクト)、それは単一の戻り値であり、その戻り値 (構造) を何らかの方法で解析する必要があります。

複数の戻り値の真の利点は、アップグレード プロセスにあります。例えば、

もともと、あなたは

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つのケースに対する代替ソリューションを持っている可能性があります:

  • スムーズなアップグレードをサポートします。これは上記の例に示されています。
  • クライアント側のコードをよりシンプルにします。私がこれまでに持っている次の代替ソリューションを参照してください。例外を使用すると、アップグレード プロセスがスムーズになりますが、コードのコストが高くなります。

現在の代替案: (それらは「複数の値を返す」構造ではありませんが、上記のポイントのいくつかを満たすエンジニアリング ソリューションになる可能性があります)

  1. タプル、リスト、辞書、オブジェクト。言われているように、クライアント側からの特定の解析が必要です。例えばif ret.success == True: blablaret = func()その前に必要です。書く方がはるかにきれいif func() == True: blabalです。
  2. を使用しExceptionます。このスレッドで説明されているように、「False」のケースがまれな場合、それは良い解決策です。この場合でも、クライアント側のコードはまだ重すぎます。
  3. などの引数を使用しdef func(main_arg, detail=[])ます。は、デザインに応じてオブジェクトにすることも、オブジェクトにすることもdetailできます。は元の単純な値のみを返します。詳細は引数に移動します。問題は、クライアントが詳細を保持するために、呼び出しの前に変数を作成する必要があることです。listdictfunc()detail
  4. 「詳細」インジケータを使用しdef func(main_arg, verbose=False)ます。verbose == False(デフォルト; クライアントの使用方法) の場合func()、元の単純な値を返します。の場合verbose == True、単純な値と詳細を含むオブジェクトを返します。
  5. 「バージョン」インジケータを使用します。「verbose」と同じですが、そこにアイデアを拡張します。このようにして、返されたオブジェクトを複数回アップグレードできます。
  6. グローバルを使用しdetail_msgます。これは古い C スタイルのようなものerror_msgです。このようにして、関数は常に単純な値を返すことができます。クライアント側はdetail_msg必要に応じて参照できます。detail_msgユース ケースに応じて、グローバル スコープ、クラス スコープ、またはオブジェクト スコープに配置できます。
  7. ジェネレーターを使用します。yield simple_returnそしてyield detailed_return。この解決策は、呼び出し先側に適しています。ただし、呼び出し元はfunc().next()andのようなことをしなければなりませんfunc().next().next()。オブジェクトでラップし、 をオーバーライドし__call__て少し単純化できます (例: func()())。ただし、呼び出し側から見ると不自然に見えます。
  8. 戻り値にはラッパー クラスを使用します。クラスのメソッドをオーバーライドして、元の単純な戻り値の動作を模倣します。詳細なデータをクラスに入れます。私たちのプロジェクトでは、bool戻り値の型を扱う際にこの代替手段を採用しました。関連するコミットを参照してください: https://github.com/fqj1994/snsapi/commit/589f0097912782ca670568fe027830f21ed1f6fc(投稿にリンクを追加するのに十分な評判がありません... ---//)

ここにいくつかの解決策があります:

  • @yupbank の回答に基づいて、デコレーターに形式化しました。github.com/hupili/multiret
  • 上記の 8 番目の選択肢は、クラスをラップできることを示しています。これが、私たちが採用した現在のエンジニアリング ソリューションです。より複雑な戻り値をラップするために、メタ クラスを使用して必要なラッパー クラスをオンデマンドで生成することができます。試したことはありませんが、これは堅牢なソリューションのように思えます。
4

3 に答える 3

1

調べてみる?

私はいくつか試してみましたが、あまりエレガントではありませんが、少なくとも実行可能です..そして動作します:)

import inspect                                                                    
from functools import wraps                                                       
import re 


def f1(*args):                                                                    
  return 2                                                                      

def f2(*args):                                                                    
  return 3, 3                                                                   

PATTERN = dict()                                                                  
PATTERN[re.compile('(\w+) f()')] = f1                                             
PATTERN[re.compile('(\w+), (\w+) = f()')] = f2                                    

def execute_method_for(call_str):                                                 
  for regex, f in PATTERN.iteritems():                                          
    if regex.findall(call_str):                                               
        return f()                                                            

def multi(f1, f2):                                                                
  def liu(func):                                                                
    @wraps(func)                                                              
    def _(*args, **kwargs):                                                   
        frame,filename,line_number,function_name,lines,index=\                
            inspect.getouterframes(inspect.currentframe())[1]                 
        call_str = lines[0].strip()                                           
        return execute_method_for(call_str)                                   
    return _                                                                  
return liu                                                                    

@multi(f1, f2)                                                                    
def f():                                                                          
  return 1                                                                      



if __name__ == '__main__':                                                        
  print f()                                                                     
  a, b = f()                                                                    
  print a, b             
于 2013-07-16T15:25:52.663 に答える
0

魔法は、結果を処理するときに実際の操作を使用しないように設計パターン blablabla を使用する必要があることですが、操作方法としてパラメーターを使用する場合は、次のコードを使用できます。

def x():
    #return 1
    return 1, 'x'*1

def f(op, f1, f2):
    print eval(str(f1) + op + str(f2))

f('+', x(), x())

より複雑な状況で一般的なソリューションが必要な場合は、f 関数を拡張し、op パラメーターを介してプロセス操作を指定できます。

于 2013-07-15T07:41:25.607 に答える