はじめに
戻り値がタプル型の関数を作ったとき、例えば
def calc_ops(a, b):
sum = a+b
diff =a-b
mul = a*b
div = a/b if b else None
return sum,diff,mul,div
この関数を実行するためには
sum ,diff ,mul ,div = calc_ops(2, 4)
_ ,diff ,_ ,_ = calc_ops(2, 4)
このように関数の戻り値の数と同じ数の変数を用意しないと関数が実行できずにエラーとなります(アンパックの数不一致エラー)。
diffだけの結果が欲しいときに、他の3つの枠を考えないといけないのは煩わしく感じます。
解決策
そこで参照値渡し出力パラメータとdataclassを使うと
from dataclasses import dataclass
@dataclass
class Result:
sum: float = 0.0
diff: float = 0.0
mul: float = 0.0
div: float = 0.0
def calc_ops(a, b, out: Result):
out.sum = a + b
out.diff = a - b
out.mul = a * b
out.div = a / b if b else None
calc = Result()
calc_ops(2, 4, calc)
print(calc) # -> Result(sum=6, diff=-2, mul=8, div=0.5)
print(calc.sum) # -> 6
戻り値の構造はdataclassで定義した形であることを保証しつつ
calc.sum や calc.divのように一つの変数だけを利用できるようになります。
また、同じResult型を別関数でも使い回せるという再利用性も高いです。
まとめ
このcalc_ops() は
-
戻り値を持たない void関数
-
構造体(dataclass)を参照で更新する副作用関数
-
戻り値を直接返さず、明確な「出力用オブジェクト」を渡す設計
つまりC++やVEXの void calc_ops(float a, float b; Result &out) に完全対応してる設計となっています。
追記
a = [1, 2, 3]
b = a
a = [4, 5, 6]
print(f"代入 {b}") #-> 代入 [1, 2, 3]
a = [1, 2, 3]
b = a
a[:] = [4, 5, 6]
print(f"同じ参照先の中身を書き換えた結果 {b}") # -> [4, 5, 6]
指摘を頂いて参照という概念がPythonにあることを初めて知りました。
C++の学習をしてもっと深く知りたくなりました。