874

それをサポートする言語で複数の値を返す標準的な方法はよくありますサンプリング

オプション:タプルを使う

この簡単な例を考えてください。

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

ただし、返される値の数が増えるにつれて、これはすぐに問題になります。 4つか5つの値を返したい場合はどうしますか?確かに、それらを組み合わせていくことはできますが、どの値がどこにあるかを忘れがちです。あなたがそれらを受け取りたいと思うところはどこでもそれらを開梱することもかなり醜いです。

オプション:辞書を使う

次の論理的なステップはある種の 'record notation'を導入することにあるようです。 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(object):
  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

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

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__インスタンスごとには作成されません。

オプション:リストを使う

私が見落としていたもう一つの提案はビルトカゲから来ている:

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

これは私の最も好きな方法です。私はHaskellに触れることに汚染されていると思いますが、混合型リストのアイデアは常に私にとって不快に感じました。この特定の例では、リストは混合型ではありませんが、それも可能性があります。このように使われるリストは、私が言うことができる限り、タプルに関して実際には何も得られません。 Pythonのリストとタプルの唯一の本当の違いは、リストが変わりやすいタラスはそうではありません。私は個人的には関数型プログラミングからの慣習を引き継ぐ傾向があります。同じ型の要素をいくつでもリストに使用し、あらかじめ決められた型の要素を固定数だけ使用するためにタプルを使用します。

質問

長い序文の後、避けられない問題があります。どの方法が良いですか?

セットアップ作業が少なくて済むので、私は一般的に自分で辞書を使うようになりました。しかし、型の観点からすると、クラスルートを避けた方がよい場合があります。辞書が表すものを混同しないようにするためです。一方、Pythonコミュニティには、次のようなことを感じる人がいます。暗黙のインタフェースは明示的なインタフェースよりも優先されるべきですこれは、同じ属性が常に同じ意味を持つという規約に基本的に頼っているからです。

では、Pythonではどうやって複数の値を返すのでしょうか。


  • あなたは素晴らしい例であなたは変数を使用しますy3しかし、y3がグローバルと宣言されていない限り、これは次のようになります。NameError: global name 'y3' is not defined多分ちょうど使用しなさい3? - hetepeperfan

14 답변


557

名前付きタプルこの目的のために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ドキュメント文字列、デフォルト値、および型注釈を使用できます。

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

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

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


  • これは、OPが考慮しなかった唯一の正規構造であり、また長いタプルを管理するという彼の問題に対処しているためです。承認済みとしてマークする必要があります。 - Andy Terra
  • まあ、のための設計理論的根拠namedtuple以下のためのメモリフットプリントが小さい質量結果(DBクエリの結果など、タプルの長いリスト)。個々の項目については(問題の関数が頻繁に呼び出されない場合)、辞書とクラスも同様に問題ありません。しかし、この場合もnamedtuplesは優れた/より良いソリューションです。 - Lutz Prechelt
  • @womあなたは一行でそれを行うことができます:p=collections.namedtuple('point','x,y')(1,2) - Dave X
  • @wom:しないでください。 Pythonは独自化する努力をしませんnamedtuple定義(各呼び出しは新しいものを作成します)、namedtupleclassはCPUとメモリの両方で比較的高価であり、すべてのクラス定義は本質的に巡回参照を含みます(CPythonでは、それらが解放されるまで巡回GCの実行を待っています)。それはまたそれを不可能にしますpickleクラス(したがって、でインスタンスを使用することは不可能です)multiprocessingほとんどの場合)。私の3.6.4 x 64でクラスを作成するたびに約0.337 msを消費し、1 KB未満のメモリを占有するため、インスタンスの節約になります。 - ShadowRanger
  • @SergeStroobandt - これは標準ライブラリの一部です。組み込みではありません。 Python> = 2.6がインストールされている他のシステムにインストールされない可能性があることを心配する必要はありません。それとも、追加のコード行に反対するだけですか。 - Justin

181

小さなプロジェクトでは、タプルを使うのが一番簡単だと思います。それを管理するのが難しくなり過ぎると(そして以前はそうではありませんが)私は物事を論理構造にグループ化し始めますが、辞書とReturnValueオブジェクトのあなたの提案した使い方は間違っていると思います

キーy0、y1、y2などを持つ辞書を返すことは、タプルを超える利点を提供しません。プロパティ.y0 .y1 .y2などを持つReturnValueインスタンスを返すことは、タプルよりも有利な点はありません。あなたがどこにでも行きたいのなら、物事の命名を始める必要があります、そしてそれでもタプルを使ってそれをすることができます:

def getImageData(filename):
  [snip]
  return size, (format, version, compression), (width,height)
size, type, dimensions = getImageData(x)

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


  • 質問 - 違いはありますかsize, type, dimensions = getImageData(x)そして(size, type, dimensions) = getImageData(x)?つまり、タプル割り当ての左辺をラップしても違いはありますか? - Reb.Cabin
  • @ Reb.Cabin違いはありません。タプルはコンマで識別され、括弧の使用は物事をまとめることです。例えば(1)intです。(1,)または1,タプルです。 - phil
  • キーy0、y1、y2などを使用して辞書を返すことは、タプルよりも有利ではありません。辞書には、既存のコードを壊すことなくフィールドを返された辞書に追加できるという利点があります。 - ostrokach

151

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

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

を与えます:

True
False


  • あなたはまだコレクションを返しています。それはタプルです。私はそれをより明確にするために括弧を好む。これを試して:type(f())戻る<class 'tuple'>。 - Igor
  • @イゴール:作る理由はありませんtupleアスペクト明示返品することは重要ではありません。tuple、これは複数値の期間を返すための慣用句です。スワップイディオムで親を省略するのと同じ理由で、x, y = y, x、複数の初期化x, y = 0, 1など確かにそれは作るtupleしかし、それを明示的にする理由はありません。tupleポイントはまったくありません。 Pythonチュートリアル多重代入を導入しますそれが触れさえするずっと前にtuples。 - ShadowRanger

62

私はその辞書に投票します。

2〜3個以上の変数を返す関数を作成すると、辞書にまとめられることがわかりました。さもなければ私は私が戻ってきたものの順序と内容を忘れがちです。

また、「特別な」構造を導入すると、コードを理解しにくくなります。 (他の誰かがコードを検索してそれが何であるかを調べる必要があります)

型が気になる場合は、「x-values list」などの説明的な辞書キーを使用してください。

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


  • 長年のプログラミングの後、私はデータと機能の構造が今までに何が必要とされるかに向かっている傾向があります。最初に機能し、必要に応じていつでもリファクタリングできます。 - monkut
  • 関数を何度も呼び出さずに辞書内の値を取得するにはどうすればよいでしょうか。たとえば、y1とy3を異なる関数で使いたい場合はどうすればいいですか? - Matt
  • 結果を別の変数に割り当てます。result = g(x); other_function(result) - monkut
  • @ monkutはい。この方法では、結果の特定の部分を毎回明確に参照しなくても、結果から異なる引数を取る複数の関数に結果を渡すこともできます。 - Gnudiff

33

別の選択肢はジェネレータを使うことです。

>>> 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タプルが通常は最良ですが、返される値がクラス内のカプセル化の候補である場合を除きます。


  • これは最もきれいな解決方法のように思われ、そしてきれいな構文を持っています。これに何か欠点がありますか?すべての返品を使用しない場合は、未使用のままになっていますか。あなたを傷つけるのを待っている利回り? - Jiminion
  • これは「きれい」になるかもしれませんが、直感的には思えません。自動タプルアンパックを行うとそれぞれがこのパターンに遭遇することを知る前に、このパターンに遭遇したことがない人がどのようにして知っているのでしょうyield? - CoreDumpError

25

タプルが「自然」と感じるときはいつでもタプルを使用することを好みます。座標は典型的な例で、ここでは別々のオブジェクトはそれら自身の上に立つことができます。 1軸のみのスケーリング計算では、順序は重要です。注:グループの意味に悪影響を及ぼさずにアイテムをソートまたはシャッフルできる場合は、おそらくタプルを使用しないでください。

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

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


22

私は好きです

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

他のものはすべて同じことをするための追加のコードにすぎません。


  • タプルは解凍するのが簡単です:y0、y1、y2 = g()あなたがしなければならない辞書:result = g()y0、y1、y2 = result.get(&#39; y0&#39;)、result。 get(&#39; y1&#39;)、result.get(&#39; y2&#39;)これは少し見苦しいです。各ソリューションには、それぞれの&#39;プラス&#39;があります。とそのマイナス(&#39;) - Oli

22

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


  • @edouardいいえリストではなくタプルを返します。 - Simon Hibbs
  • 破壊は私の意見ではリストを返すための議論 - semiomant

16

Pythonのタプル、辞書、およびオブジェクトは、プログラマーに、形式と簡潔なデータ構造(「もの」)の利便性との間の円滑なトレードオフを提供します。私にとって、ものを表現する方法の選択は、主にその構造をどのように使用するかによって決まります。 C ++では、使用するのが一般的な規約です。structデータのみの項目の場合classメソッドを持つオブジェクトに対しては、メソッドに合法的にメソッドを置くことはできますがstruct;私の習慣はPythonでも似ています。dictそしてtuple代わりにstruct

座標セットには、tuple点ではなくclassまたはdict(そしてあなたが使用できることに注意してくださいtuple辞書のキーとして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シリアライゼーションに最も適しているからです。


15

一般的に、「特殊な構造」は実際にはオブジェクトの賢明な現在の状態であり、独自のメソッドがあります。

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

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


14

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

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


3

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

しかし、私が自分自身が同じ変数のセットを絶えず返すのであれば、それはおそらく私が除外する新しいクラスを含みます。


3

関数から値を渡したり返したりするには、辞書を使用します。

で定義されているように変数形式を使用する

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つだけポインタを戻す必要があります。

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


2

「最高」は部分的に主観的な決定です。不変が許容できる一般的な場合には、小さな戻り値集合にタプルを使用します。可変性が必要条件ではない場合、タプルは常にリストよりも優先されます。

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

賢人が言ったように:

時期尚早の最適化は、すべての悪の根源です(または少なくともほとんどの   それは)プログラミングで。

リンクされた質問


関連する質問

最近の質問