web-dev-qa-db-ja.com

関数から複数の値を返す方法は?

それをサポートする言語で複数の値を返す標準的な方法は、しばしば tupling です。

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

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

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 }はちょうどReturnValueの内部の__dict__のエントリになってしまいます。

小さなオブジェクトのために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__が作成されないため、スペースが節約されます。

オプション: dataclass を使う(python 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)

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

私が見逃していたもう一つの提案はBill the Lizardから来ています。

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

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

質問

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

セットアップ作業が少なくて済むので、私は一般的に自分で辞書を使うことに気づきました。しかし、型の観点からすると、クラスルートを迂回するほうが得策かもしれません。辞書が表すものを混同しないようにするためです。一方、Pythonコミュニティには、 暗黙のインタフェースは明示的なインタフェース よりも優先されるべきであると考える人がいます。同じ属性が常に同じ意味を持つという規則。

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

929
saffsd

名前付きタプル この目的のために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
592
A. Coady

小さなプロジェクトでは、タプルを使った作業が最も簡単だと思います。それを管理するのが難しくなり過ぎると(そして以前はそうではなく)、私は物事を論理構造にグループ化し始めますが、辞書と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)から得られるように、適切なメソッドとプロパティを持つ実際のオブジェクトを返すことです。

193
too much php

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

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

を与えます:

True
False
178
Joseph Hansen

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

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

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

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

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

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

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

35
rlms

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

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

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

27
tzot

私は好きです

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

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

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

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

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

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

18
S.Lott

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

座標セットには、ポイントTupleclassではなくdictを使用します(Tupleを辞書キーとして使用できるので、dictを使用するとスパース多次元配列を作成できます)。

物事のリストを反復処理する場合は、反復処理でTuplesを解凍することをお勧めします。

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以外のシステムコンポーネントとデータを交換する場合、JSONシリアライゼーションに最も適しているので、ほとんどの場合、それらをdictに保持します。

18

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

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

15
John Fouhy

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

しかし、常に同じ変数のセットを返しているのであれば、おそらく新しいクラスが必要になるでしょう。

4
fluffels

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

form で定義されているように変数formを使用してください。

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

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

3
Elis Byberi

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

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

賢人が言ったように:

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

1
joel3000