複数の値を返すことをサポートしている言語での典型的な方法は、しばしばtuplingです。
次のような些細な例を考えてみましょう。
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:
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)
結局のところ { 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リファレンスマニュアル]2より。
__slots__
宣言は、一連のインスタンス変数を受け取り、各変数の値を保持するのに十分なスペースを各インスタンスに確保します。インスタンスごとに__dict__
を作成しないので、スペースが節約できます。
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に触れたことで汚染されてしまったのかもしれませんが、型の混在したリストのアイデアはいつも私にとって不快なものでした。この例では、リストは混合型ではありませんが、混合型になる可能性もあります。
このように使われたリストは、私の知る限り、タプルに対して何の得にもなりません。Pythonにおけるリストとタプルの唯一の違いは、リストがmutableであるのに対し、タプルはそうではないということです。
私は個人的に、関数型プログラミングからの慣習を引き継ぐ傾向にあります。つまり、同じ型の要素がいくつあってもリストを使い、あらかじめ決められた型の要素がいくつあってもタプルを使うのです。
前置きが長くなったところで、避けては通れない質問です。あなたはどの方法が一番良いと思いますか?
私は、セットアップの手間がかからないという理由で、通常、辞書のルートを利用しています。しかし、タイプの観点から言えば、クラスのルートを取った方が良いかもしれません。
一方で、Pythonコミュニティの中には、明示的なインターフェースよりも暗黙的なインターフェースを優先すべきと考える人もいますが、その場合、オブジェクトの型は本当に関係ありません。
では、Pythonで複数の値を返すにはどうすればよいのでしょうか?
一般的に、「特殊な構造」は、実際にはオブジェクトの感覚的な現在の状態であり、それ自身のメソッドを持っています。
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
私は可能な限り、無名の構造体の名前を見つけるのが好きです。 意味のある名前は物事をより明確にします。