関数が複数の値を返すのはpythonicですか?


81

Pythonでは、関数に複数の値を返すようにすることができます。不自然な例を次に示します。

def divide(x, y):
    quotient = x/y
    remainder = x % y
    return quotient, remainder  

(q, r) = divide(22, 7)

これは非常に便利なようですが、悪用される可能性もあるようです(「関数Xは、必要なものを中間値としてすでに計算しています。Xにその値も返させましょう」)。

いつ線を引き、別の方法を定義する必要がありますか?

回答:


107

絶対に(あなたが提供した例のために)。

タプルはPythonの第一級市民です

divmod()まさにそれを行う組み込み関数があります。

q, r = divmod(x, y) # ((x - x%y)/y, x%y) Invariant: div*y + mod == x

他の例があります:zipenumeratedict.items

for i, e in enumerate([1, 3, 3]):
    print "index=%d, element=%s" % (i, e)

# reverse keys and values in a dictionary
d = dict((v, k) for k, v in adict.items()) # or 
d = dict(zip(adict.values(), adict.keys()))

ところで、括弧はほとんどの場合必要ありません。Pythonライブラリリファレンスからの引用:

タプルは、いくつかの方法で構築できます。

  • 空のタプルを示すために括弧のペアを使用する:()
  • シングルトンタプルの末尾のコンマの使用:a、または(a、)
  • アイテムをコンマで区切る:a、b、cまたは(a、b、c)
  • 組み込みのtuple()の使用:tuple()またはtuple(iterable)

関数は単一の目的を果たす必要があります

したがって、単一のオブジェクトを返す必要があります。あなたの場合、このオブジェクトはタプルです。タプルをアドホックな複合データ構造と見なします。ほとんどすべての関数が複数の値を返す言語があります(Lispのリスト)。

(x, y)代わりに戻るだけで十分な場合もありPoint(x, y)ます。

名前付きタプル

Python 2.6での名前付きタプルの導入により、多くの場合、プレーンタプルではなく名前付きタプルを返すことが望ましいです。

>>> import collections
>>> Point = collections.namedtuple('Point', 'x y')
>>> x, y = Point(0, 1)
>>> p = Point(x, y)
>>> x, y, p
(0, 1, Point(x=0, y=1))
>>> p.x, p.y, p[0], p[1]
(0, 1, 0, 1)
>>> for i in p:
...   print(i)
...
0
1

1
そのようなタプルを使用する機能はとても便利です。私はJavaでその能力を持っていることを本当に懐かしく思っています。
MBCook 2009

3
名前付きタプルについて言及していただき、ありがとうございます。私はこのようなものを探していました。
vobject 2009

戻り値が制限されている場合、dict()はより単純/高速になる可能性があります(特に、関数がREST APIおよびJSONに使用される場合)。私のシステムでは、def test_nt():Point = namesdtuple( 'Point'、['x'、 'y'])p = Point(10.5、11.5)json.dumps(p._asdict())は819μs/ループかかります、def test_dict():d = {'x':10.5、 'y':11.5} json.dumps(d)は15.9μs/ループを要します....したがって> 50倍高速
comte

@comte:はい、。Point(10.5, 11.5)より6倍遅いです{'x': 10.5, 'y':11.5}。絶対時間は635 ns +- 26 ns105 ns +- 4 nsです。どちらかがアプリケーションのボトルネックになる可能性はほとんどありません。プロファイラーが特に指示しない限り、最適化しないでください。なぜ必要なのかわからない限り、関数レベルでクラスを作成しないでください。APIがdict使用するよりも必要な場合、dictそれはパフォーマンスとは何の関係もありません。
jfs 2017年

27

まず、Pythonでは次のことが可能であることに注意してください(括弧は必要ありません)。

q, r = divide(22, 7)

あなたの質問に関しては、どちらの方法でも厳格なルールはありません。単純な(そして通常は考案された)例の場合、特定の関数が単一の目的を持ち、単一の値になることは常に可能であるように思われるかもしれません。ただし、実際のアプリケーションにPythonを使用すると、複数の値を返す必要がある多くのケースにすぐに遭遇し、コードがよりクリーンになります。

ですから、私は理にかなっていることは何でもし、人為的な慣習に従おうとしないでください。Pythonは複数の戻り値をサポートしているため、必要に応じて使用してください。


4
コンマはタプルを作成するため、expression:q, r タプルです。
jfs 2008

Vinko、例は標準ライブラリのtempfile.mkstemp()で、ファイルハンドルと絶対パスを含むタプルを返します。JFS、はい、その通りです。
Jason Etheridge

しかし、それは単一の目的です...単一の目的と複数の戻り値を持つことができます
Vinko Vrsalovic 2008

他の言語が複数の戻り値の型を持つ可能性がある場合、参照と「out」パラメーターにとらわれることはありません。
orip 2008

複数の値を返すことは、Pythonの最大の機能の1つです。
マーティンベケット

13

あなたが与える例は、実際にはと呼ばれるpython組み込み関数divmodです。そのため、ある時点で、コア機能に含めるのに十分なPythonであると誰かが考えました。

私にとって、それがコードをよりクリーンにするなら、それはpythonicです。これらの2つのコードブロックを比較します。

seconds = 1234
minutes, seconds = divmod(seconds, 60)
hours, minutes = divmod(minutes, 60)

seconds = 1234
minutes = seconds / 60
seconds = seconds % 60
hours = minutes / 60
minutes = minutes % 60

3

はい、複数の値(つまり、タプル)を返すことは間違いなくpythonicです。他の人が指摘しているように、Python標準ライブラリや評判の高いPythonプロジェクトにはたくさんの例があります。2つの追加コメント:

  1. 複数の値を返すと、非常に便利な場合があります。たとえば、オプションでイベントを処理し(そうすることで何らかの値を返す)、成功または失敗も返すメソッドを考えてみましょう。これは、一連の責任パターンで発生する可能性があります。それ以外の場合は、与えられた例のように、密接にリンクされた複数のデータを返したい場合があります。この設定では、複数の値を返すことは、複数のメンバー変数を持つ匿名クラスの単一のインスタンスを返すことに似ています。
  2. Pythonのメソッド引数の処理には、複数の値を直接返す機能が必要です。たとえば、C ++では、メソッド引数を参照で渡すことができるため、正式な戻り値に加えて、出力値を割り当てることができます。Pythonでは、引数は「参照によって」渡されます(ただし、C ++ではなくJavaの意味で)。メソッド引数に新しい値を割り当てて、メソッドスコープ外に反映させることはできません。例えば:

    // C++
    void test(int& arg)
    {
        arg = 1;
    }
    
    int foo = 0;
    test(foo); // foo is now 1!
    

    と比べて:

    # Python
    def test(arg):
        arg = 1
    
    foo = 0
    test(foo) # foo is still 0
    

「オプションでイベントを処理し、成功または失敗を返すメソッド」-それが例外です。
ネイサン

例外は「例外的な」条件のみであり、単なる成功または失敗ではないことは非常に広く受け入れられています。議論の一部については、Googleの「エラーコードと例外」。
zweiterlinde 2010年

1

それは間違いなくpythonicです。どこかで返す型のすべての組み合わせに対して構造体を定義する必要があるCのような言語で、ボイラープレートの関数から複数の値を返すことができるという事実。

ただし、1つの関数から10個の値のようなクレイジーなものを返すようになった場合は、その時点で扱いにくくなるため、クラスにバンドルすることを真剣に検討する必要があります。


1

タプルを返すのはかっこいいです。また、Python 2.6で追加された新しいnamedtupleにも注意してください。これにより、これがより快適になる可能性があります:http://docs.python.org/dev/library/collections.html#collections.namedtuple


1

OT:RSREのAlgol68には、奇妙な "/:="演算子があります。例えば。

INT quotient:=355, remainder;
remainder := (quotient /:= 113);

商を3、余りを16にします。

注:通常、「(x /:= y)」の値は、商「x」が参照によって割り当てられるため破棄されますが、RSREの場合、戻り値は剰余です。

整数演算を参照-Algol68


0

などの単純な関数のタプルを使用して複数の値を返すことは問題ありませんdivmod。コードが読みやすくなる場合は、Pythonicです。

戻り値が混乱し始めた場合は、関数が実行しすぎていないかどうかを確認し、実行している場合は分割します。大きなタプルがオブジェクトのように使用されている場合は、それをオブジェクトにします。また、Python2.6の標準ライブラリの一部となる名前付きタプルの使用を検討してください。


0

私はPythonにかなり慣れていませんが、タプル手法は私には非常にPython的であるように見えます。しかし、読みやすさを向上させる可能性のある別のアイデアがありました。辞書を使用すると、位置ではなく名前でさまざまな値にアクセスできます。例えば:

def divide(x, y):
    return {'quotient': x/y, 'remainder':x%y }

answer = divide(22, 7)
print answer['quotient']
print answer['remainder']

2
これをしないでください。結果をワンライナーで割り当てることはできません。非常に不格好です。(結果を保存したい場合を除いて、その場合はこれをメソッドに入れる方が良いでしょう。)dictの目的が戻り値を自己文書化することである場合は、a)fnに合理的に説明的な名前を付けます(「 divmod ")およびb)残りの説明をdocstring(Pythonicで配置する場所)に入れます。そのdocstringが2行より長い必要がある場合、それは関数がテーマ的にオーバーロードされていることを示唆しており、悪い考えかもしれません。
smci 2011年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.