関数から複数の値を返すにはどうすればよいですか?[閉まっている]


1067

それをサポートする言語で複数の値を返すための標準的な方法は、しばしば混乱しています。

オプション:タプルの使用

次の簡単な例を考えてみます。

def f(x):
  y0 = x + 1
  y1 = x * 3
  y2 = y0 ** y3
  return (y0, y1, y2)

ただし、返される値の数が増えると、すぐに問題が発生します。4つまたは5つの値を返す場合はどうなりますか?もちろん、それらを繰り返し使用することはできますが、どの値がどこにあるのかを簡単に忘れてしまいます。また、受け取りたい場所をどこでも開梱するのは見苦しくなります。

オプション:辞書を使用する

次の論理的なステップは、ある種の「レコード表記」を導入することです。Pythonでは、これを行う明白な方法はを使用することですdict

以下を検討してください。

def g(x):
  y0 = x + 1
  y1 = x * 3
  y2 = y0 ** y3
  return {'y0': y0, 'y1': y1 ,'y2': y2}

(明確にするために、y0、y1、およびy2は単に抽象的な識別子として意図されています。指摘したように、実際には、意味のある識別子を使用します。)

これで、返されたオブジェクトの特定のメンバーを投影できるメカニズムができました。例えば、

result['y0']

オプション:クラスの使用

ただし、別のオプションがあります。代わりに、特殊な構造を返すことができます。私はこれをPythonのコンテキストで組み立てましたが、他の言語にも適用できると確信しています。確かに、もしあなたがCで働いていたなら、これが非常によくある唯一の選択肢かもしれません。ここに行く:

class ReturnValue:
  def __init__(self, y0, y1, y2):
     self.y0 = y0
     self.y1 = y1
     self.y2 = y2

def g(x):
  y0 = x + 1
  y1 = x * 3
  y2 = y0 ** y3
  return ReturnValue(y0, y1, y2)

Pythonでは、前の2つはおそらく配管の点で非常に似{ y0, y1, y2 }ています-結局のところ、最終的__dict__にはの内部のエントリになりますReturnValue

小さなオブジェクトである__slots__属性には、Pythonによって提供される追加機能が1つあります。クラスは次のように表現できます。

class ReturnValue(object):
  __slots__ = ["y0", "y1", "y2"]
  def __init__(self, y0, y1, y2):
     self.y0 = y0
     self.y1 = y1
     self.y2 = y2

Pythonリファレンス・マニュアル

__slots__宣言は、各変数の値を保持するために、各インスタンスにインスタンス変数と埋蔵量だけで十分なスペースのシーケンスを取り。は__dict__インスタンスごとに作成されないため、スペースが節約されます。

オプション:使用するデータクラスを(パイソン3.7+)

Python 3.7の新しいデータクラスを使用して、自動的に追加された特別なメソッド、入力、その他の便利なツールを備えたクラスを返します。

@dataclass
class Returnvalue:
    y0: int
    y1: float
    y3: int

def total_cost(x):
    y0 = x + 1
    y1 = x * 3
    y2 = y0 ** y3
    return ReturnValue(y0, y1, y2)

オプション:リストを使用する

私が見逃していたもう1つの提案は、ビルザトカゲからのものです。

def h(x):
  result = [x + 1]
  result.append(x * 3)
  result.append(y0 ** y3)
  return result

これは私の最も好きな方法ですが。私はHaskellへの暴露に汚染されていると思いますが、混合タイプのリストの考えは常に不快に感じてきました。この特定の例では、リストは混合タイプではありませんが、そうである可能性があります。

この方法で使用されるリストは、私が知る限り、タプルに関して実際には何も得ません。Pythonでのリストとタプルの唯一の本当の違いは、リストは変更可能であるのに対し、タプルは変更できないことです。

私は個人的に、関数型プログラミングの慣例を引き継ぐ傾向があります。同じタイプの要素をいくつでもリストに使用し、事前に定義されたタイプの要素をいくつでもタプルを使用します。

質問

長いプリアンブルの後に、避けられない質問が続きます。どちらの方法が良いと思いますか?


10
あなたの優れた例では変数を使用しますがy3、y3がグローバルとして宣言されていない限り、これはNameError: global name 'y3' is not definedおそらく単に3
hetepeperfan 2013

11
「意見」キーワードが発生するため、優れた回答を伴う多くの素晴らしい質問は閉じられます。SO全体は意見に基づいていると主張することもできますが、それは事実、参照、および特定の専門知識によって通知された意見です。誰かが「どちらが良いと思いますか」と尋ねたからといって、実際の事実、参考文献、特定の専門知識から抽出された個人的な意見を求めているわけではありません。彼らはほぼ間違いなく、そのような意見を正確に求めています。そのような意見は、人が意見を形成するために使用した事実、参照、および特定の専門知識に完全に基づいて文書化されたものです。
NeilG

@hetepeperfan 3を変更する必要はなく、どちらもグローバルでy3を定義y3していません。ローカル名を使用することもできます。これでも同じことができます。
okie

回答:


637

名前付きタプルは、この目的のために2.6で追加されました。同様の組み込み例については、os.statも参照してください。

>>> import collections
>>> Point = collections.namedtuple('Point', ['x', 'y'])
>>> p = Point(1, y=2)
>>> p.x, p.y
1 2
>>> p[0], p[1]
1 2

最近のバージョンのPython 3(3.6以上、と思います)では、新しいtypingライブラリーにNamedTupleクラスが追加され、名前付きタプルの作成が簡単になり、より強力になりました。から継承typing.NamedTupleすると、docstring、デフォルト値、および型注釈を使用できます。

例(ドキュメントから):

class Employee(NamedTuple):  # inherit from typing.NamedTuple
    name: str
    id: int = 3  # default value

employee = Employee('Guido')
assert employee.id == 3

68
これは正解です。OPが考慮しなかった唯一の正規構造であり、長いタプルを管理するという彼の問題に対処するためです。承認済みとしてマークする必要があります。
2014

7
まあ、の設計の根拠namedtupleは、大量の結果(DBクエリの結果などのタプルの長いリスト)のメモリフットプリントが小さいことです。個々のアイテム(問題の関数が頻繁に呼び出されない場合)についても、辞書とクラスは問題ありません。ただし、この場合も、namedtupleは優れた/優れたソリューションです。
Lutz Prechelt、2016

8
@wom:これを行わないでください。Pythonはnamedtuple定義を一意化する努力をしません(各呼び出しは新しい定義を作成します)。namedtupleクラスの作成はCPUとメモリの両方で比較的高価であり、すべてのクラス定義は本質的に循環参照を含みます(したがって、CPythonでは、循環GCの実行を待っています)それらが解放されるために)。またpickle、クラスを不可能にします(したがって、multiprocessingほとんどの場合でインスタンスを使用できません)。3.6.4 x64でクラスを作成するたびに、約0.337ミリ秒を消費し、1 KB未満のメモリを占有するため、インスタンスの節約がすべて失われます。
ShadowRanger

3
注意しますが、Python 3.7 新しいnamedtupleクラスの作成速度が向上しましたCPUコストは約4倍減少しますが、インスタンスを作成するコストよりも約1000倍高く、各クラスのメモリコストは高いままです(「1 KB未満」に関する前回のコメントでは間違っていました)クラスの場合_source、それ自体は通常1.5 KBです_sourceが、3.7で削除されているため、クラスの作成ごとに1 KB未満という当初の主張に近い可能性があります。
ShadowRanger

4
@SergeStroobandt-これは標準ライブラリの一部であり、組み込みではありません。Pythonが2.6以上の別のシステムにインストールされないことを心配する必要はありません。それとも、余分なコード行に異議を唱えますか?
ジャスティン

234

小規模なプロジェクトの場合、タプルを使用するのが最も簡単です。それを管理するのが非常に難しくなると(以前はそうではありません)、物事を論理構造にグループ化し始めますReturnValue

キーを持つ辞書を返す"y0""y1""y2"、などのタプル上の任意の利点を提供していません。返すReturnValueプロパティを持つインスタンスを.y0.y1.y2、などのいずれかの組の上に任意の利点を提供していません。どこにでも行きたいなら、名前を付ける必要があります。とにかくタプルを使ってそれを行うことができます:

def get_image_data(filename):
    [snip]
    return size, (format, version, compression), (width,height)

size, type, dimensions = get_image_data(x)

IMHO、タプルを超える唯一の優れたテクニックは、re.match()またはから取得するような適切なメソッドとプロパティを持つ実際のオブジェクトを返すことですopen(file)


6
質問-とに違いはsize, type, dimensions = getImageData(x)あり(size, type, dimensions) = getImageData(x)ますか?つまり、タプルされた割り当ての左側をラップすることで何か違いはありますか?
Reb.Cabin 2014

11
@ Reb.Cabin違いはありません。タプルはカンマで識別され、括弧の使用は物事をグループ化するためだけです。たとえば(1)、int while (1,)または1,tupleです。
2015年

19
「キーy0、y1、y2などのディクショナリを返すことはタプルよりも優れているわけではありません」:ディクショナリには、既存のコードを壊すことなく、返されたディクショナリにフィールドを追加できるという利点があります。
ostrokach

「キーy0、y1、y2などのディクショナリを返すことはタプルよりも優れているわけではありません」:位置ではなく名前に基づいてデータにアクセスするため、読みやすく、エラーが発生しにくくなります。
Denis Dollfus

204

答えの多くは、辞書やリストなど、ある種のコレクションを返す必要があることを示唆しています。追加の構文を省略して、戻り値をコンマ区切りで書き出すこともできます。注:これは技術的にはタプルを返します。

def f():
    return True, False
x, y = f()
print(x)
print(y)

与える:

True
False

24
あなたはまだコレクションを返しています。タプルです。私はそれをより明確にするために括弧を好む。これを試してください:をtype(f())返します<class 'tuple'>
イゴール

20
@Igor:tupleアスペクトを明示する理由はありません。を返すことはそれほど重要ではありません。tupleこれは、複数の値の期間を返すためのイディオムです。スワップイディオム、、x, y = y, x複数の初期化x, y = 0, 1などで括弧を省略する同じ理由。確かに、それはtuplesを内部で作成しtupleますが、sがまったく重要ではないため、それを明示する理由はありません。Pythonチュートリアルでは、sに触れるずっと前に複数の割り当てが導入されていますtuple
ShadowRanger

@ShadowRangerの右側にあるコンマで区切られた値のシーケンスは=、Pythonのタプルで、括弧が付いている場合と付いていない場合があります。したがって、実際には明示的または暗黙的なことはありません。a、b、cは(a、b、c)と同じタプルです。また、単純な単純なタプルであるため、このような値を返す場合、「内部」でのタプルの作成もありません。OPはすでにタプルについて言及しているため、彼が言及した内容とこの回答が示す内容との間に実際の違いはありません。なし
Ken4scholars

2
これは文字通り質問で提案された最初のオプションです
内部石19/10/26

1
@endolith男が質問する2回(「複数の値を返すにはどうすればよいですか?」と「複数の値返すにはどうすれよいですか?」)は、この回答で答えられます。質問のテキストは時々変更されました。そして、それは意見に基づく質問です。
ジョセフハンセン

74

辞書に投票します。

2〜3個を超える変数を返す関数を作成すると、辞書に折りたたまれます。さもなければ、私が返すものの順序と内容を忘れがちです。

また、「特別な」構造を導入すると、コードの追跡が難しくなります。(他の誰かがコードを検索して、それが何かを見つける必要があります)

タイプの検索が気になる場合は、「x値リスト」などの説明的な辞書キーを使用してください。

def g(x):
  y0 = x + 1
  y1 = x * 3
  y2 = y0 ** y3
  return {'y0':y0, 'y1':y1 ,'y2':y2 }

5
長年のプログラミングの後、私はデータと機能の構造が必要とされるものに向かう傾向があります。最初に機能し、必要に応じていつでもリファクタリングできます。
monkut 2014年

関数を複数回呼び出さずに、辞書内の値をどのように取得しますか?たとえば、別の関数でy1とy3を使用したい場合はどうすればよいですか?
マット

3
結果を別の変数に割り当てます。 result = g(x); other_function(result)
monkut 2014年

1
@monkutはい。この方法では、毎回結果の特定の部分を具体的に参照する必要なく、結果から異なる引数を取得する複数の関数に結果を渡すこともできます。
Gnudiff、2018年

38

別のオプションはジェネレータを使用することです:

>>> def f(x):
        y0 = x + 1
        yield y0
        yield x * 3
        yield y0 ** 4


>>> a, b, c = f(5)
>>> a
6
>>> b
15
>>> c
1296

返される値がクラスのカプセル化の候補である場合を除いて、通常はIMHOタプルが最適です。


1
これは最もクリーンなソリューションのようであり、構文はクリーンです。これには欠点がありますか?すべてのリターンを使用しない場合、あなたを傷つけるのを待っている「未使用」の収穫はありますか?
Jiminion 2015

24
これは「クリーン」かもしれませんが、直感的には思えません。タプルの自動アンパッキングを実行するとそれぞれがトリガーされることを知る前に、このパターンに遭遇したことがない人はどうしたらyieldいいでしょう?
coredumperror

1
@CoreDumpError、ジェネレーターはまさにそのジェネレーターです。そこの間には、外部の違いはありませんdef f(x): …; yield b; yield a; yield r対は(g for g in [b, a, r])、両方とも容易にリストやタプルに、そのような意志のサポートタプルを開梱として変換されます。タプルジェネレーターフォームは機能的なアプローチに従いますが、関数フォームは必須であり、フロー制御と変数の割り当てを可能にします。
sleblanc

30

タプルが「自然」であると感じるときはいつでもタプルを使用することを好みます。座標は典型的な例です。たとえば、1軸のみのスケーリング計算では、個別のオブジェクトが独立して立つことができ、順序が重要です。注:グループの意味に悪影響を与えずにアイテムを並べ替えたりシャッフルしたりできる場合は、タプルを使用しないでください。

グループ化されたオブジェクトが常に同じではない場合にのみ、辞書を戻り値として使用します。オプションのメールヘッダーを考えてみてください。

それ以外の場合、グループ化されたオブジェクトがグループ内で固有の意味を持つか、独自のメソッドを持つ完全なオブジェクトが必要な場合は、クラスを使用します。


29

私は好む:

def g(x):
  y0 = x + 1
  y1 = x * 3
  y2 = y0 ** y3
  return {'y0':y0, 'y1':y1 ,'y2':y2 }

他のすべては同じことをするための単なる追加のコードのようです。


22
タプルはアンパックが簡単です:y0、y1、y2 = g()dictを実行する必要があります:result = g()y0、y1、y2 = result.get( 'y0')、result.get( 'y1' )、result.get( 'y2')は少し醜いです。各ソリューションには「プラス」と「マイナス」があります。
Oli、

27
>>> def func():
...    return [1,2,3]
...
>>> a,b,c = func()
>>> a
1
>>> b
2
>>> c
3

@edouardいいえ、そうではありません。リストではなくタプルを返します。
Simon Hibbs

1
非構造は、私の意見ではリストを返すための引数
semiomant

21

一般に、「特殊化された構造」は、実際には、独自のメソッドを備えたオブジェクトの実用的な現在の状態です。

class Some3SpaceThing(object):
  def __init__(self,x):
    self.g(x)
  def g(self,x):
    self.y0 = x + 1
    self.y1 = x * 3
    self.y2 = y0 ** y3

r = Some3SpaceThing( x )
r.y0
r.y1
r.y2

可能な場合は、匿名の構造の名前を見つけるのが好きです。意味のある名前は、物事をより明確にします。


20

Pythonのタプル、ディクト、およびオブジェクトは、プログラマーに形式と小さなデータ構造(「もの」)の利便性の間のスムーズなトレードオフを提供します。私にとって、モノを表現する方法の選択は、主に構造をどのように使用するかによって決まります。C ++では、structデータのみのアイテムとclassメソッドを持つオブジェクトに使用するのが一般的な慣例ですが、メソッドをに合法的に置くことはできstructます。私の習慣はで、Pythonで類似しているdicttupleの代わりに、struct

座標セットのtuple場合は、点classまたはa ではなくdictを使用します(tupleaを辞書のキーとして使用できるため、dictsはスパースな多次元配列を作成します)。

物事のリストを繰り返し処理する場合はtuple、反復でsをアンパックすることをお勧めします。

for score,id,name in scoreAllTheThings():
    if score > goodScoreThreshold:
        print "%6.3f #%6d %s"%(score,id,name)

...オブジェクトのバージョンが読みやすくなるので:

for entry in scoreAllTheThings():
    if entry.score > goodScoreThreshold:
        print "%6.3f #%6d %s"%(entry.score,entry.id,entry.name)

...はもちろんdict

for entry in scoreAllTheThings():
    if entry['score'] > goodScoreThreshold:
        print "%6.3f #%6d %s"%(entry['score'],entry['id'],entry['name'])

モノが広く使用されており、コードの複数の場所で同様の重要な操作を行っている場合は、通常、適切なメソッドを持つクラスオブジェクトにすることをお勧めします。

最後に、Python以外のシステムコンポーネントとデータを交換する場合は、dictJSONシリアライゼーションに最適であるため、ほとんどの場合はそれらを保持します。


19

S.Lottの名前付きコンテナークラスの提案に対する+1。

Python 2.6以降の場合、名前付きタプルはこれらのコンテナクラスを簡単に作成するための便利な方法を提供し、結果は「軽量で通常のタプルよりも多くのメモリを必要としません」です。


4

Pythonのような言語では、新しいクラスを作成するよりもオーバーヘッドが少ないため、通常は辞書を使用します。

ただし、同じ変数のセットを常に返すことに気付いた場合は、おそらく、その中に新しいクラスが含まれています。


4

私はdictを使用して関数から値を渡したり返したりします:

で定義された変数のフォームを使用し、フォーム

form = {
    'level': 0,
    'points': 0,
    'game': {
        'name': ''
    }
}


def test(form):
    form['game']['name'] = 'My game!'
    form['level'] = 2

    return form

>>> print(test(form))
{u'game': {u'name': u'My game!'}, u'points': 0, u'level': 2}

これは私にとっても処理ユニットにとっても最も効率的な方法です。

ポインタを1つだけ渡して、1つだけポインタを返す必要があります。

コードを変更するたびに、関数の引数(数千)を変更する必要はありません。


口述は変更可能です。dictを関数に渡し、その関数がdictを編集すると、変更はその関数のスコープ外に反映されます。関数が最後にdictを返すようにすることは、関数に副作用がないことを意味する可能性があるため、値が返されるべきではなく、が値testを直接変更することを明確にします。これをdict.update、値を返さないと比較してください。
sleblanc

@sleblanc「関数が最後にdictを返すようにすることは、関数に副作用がないことを意味するかもしれません。」あなたが言ったように、dictは変更可能だからです。ただし、戻るformことで可読性やパフォーマンスが損なわれることはありません。を再フォーマットする必要がある場合、フォームの変更をどこにも追跡しないためform、[form]を返すと、必ず最後formが返されます。
Elis Byberi

3

「ベスト」は部分的に主観的な決定です。不変が許容される一般的なケースでは、小さな戻り値セットにタプルを使用します。可変性が要求されない場合、タプルは常にリストよりも望ましいです。

より複雑な戻り値の場合、または形式性が重要な場合(つまり、値の大きいコード)には、名前付きタプルの方が適しています。最も複雑なケースでは、通常オブジェクトが最適です。ただし、実際に重要なのは状況です。それが関数の最後に自然にあるのでオブジェクトを返すことが理にかなっている場合(例:Factoryパターン)、オブジェクトを返します。

賢者が言ったように:

時期尚早な最適化は、プログラミングにおけるすべての悪(または少なくともその大部分)の原因です。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.