pythonで、パラメータ、戻り値の数の異なる関数を同一インタフェースで呼び出す方法

pythonにて、パラメータ、戻り値の数が異なっても同じインタフェースで呼び出したい場合があります。同一インタフェースで呼び出す方法を検討します。

# 関数
以下の関数を考えます。

```python
def func1(x, a=2, b=3, c=4):
    print("IN  x=", x, "a=", a, "b=", b, "c=", c)
    x *= 2
    a *= 2
    b *= 2
    c *= 2
    print("OUT x=", x, "a=", a, "b=", b, "c=", c)
    return x, a, b, c
```

与えられたパラメータを２倍して返す関数です。
$=$で既定値を指定しているパラメータは省略可能パラメータ、既定値を指定しない場合は、必須のパラメータになります。

# 関数のパラメータ
## 通常の呼び出し
関数の呼び出し方法は、パラメータの順序で呼び出す方法、省略可能なパラメータに限られますが、パラメータ名を指定して呼び出す方法があります。

```python
# パラメータ順
func1(10,20,30)
```

```
結果
IN  x= 10 a= 20 b= 30 c= 4
OUT x= 20 a= 40 b= 60 c= 8
```

```python
# パラメータ名指定
func1(10, a=20, c=40)
```

```
結果
IN  x= 10 a= 20 b= 3 c= 40
OUT x= 20 a= 40 b= 6 c= 80
```

```python
# 両方
func1(10, 20, c=40)
```

```
結果
IN  x= 10 a= 20 b= 3 c= 40
OUT x= 20 a= 40 b= 6 c= 80
```

## リスト、辞書を利用した呼び出し
順序で呼び出す場合は、リストを利用できます。タプルでも可能です。
$*$をつけて呼び出します。

```python
# パラメータ順(リスト)
param = [20,30]
func1(10, *param)
```

```
結果
IN  x= 10 a= 20 b= 30 c= 4
OUT x= 20 a= 40 b= 60 c= 8
```

パラメータ名付きで呼び出す場合は、辞書を利用できます。
$**$をつけて呼び出します。

```python
# パラメータ名指定(辞書)
param = {"a":20, "c":40}
func1(10, **param)
```

```
結果
IN  x= 10 a= 20 b= 3 c= 40
OUT x= 20 a= 40 b= 6 c= 80
```

通常の呼び出しと同じになりました。

両方の場合です。

```python
# 両方
paraml = [20]
paramd = {"c":40}
func1(10, *paraml, **paramd)
```

```
結果
IN  x= 10 a= 20 b= 3 c= 40
OUT x= 20 a= 40 b= 6 c= 80
```

リスト、辞書で呼び出すことが可能なことがわかりました。

# 関数の戻り値
関数の戻り値は、カンマで区切って複数指定することが可能です。戻り値は、タプルで返却されます。

```python
print(type(func1(10)))
```

```
結果
IN  x= 10 a= 2 b= 3 c= 4
OUT x= 20 a= 4 b= 6 c= 8
<class 'tuple'>
```

## 通常の受け取り方
戻り値を順に受け取ります。

```python
x, a, b, c = func1(10)
print(x, a, b, c)
```

```
結果
IN  x= 10 a= 2 b= 3 c= 4
OUT x= 20 a= 4 b= 6 c= 8
20 4 6 8
```

特定の戻り値のみ受け取る場合は、受け取らないパラメータは、'_'とします。

```python
_, a, _, _ = func1(10)
print(a)
```

```
結果
IN  x= 10 a= 2 b= 3 c= 4
OUT x= 20 a= 4 b= 6 c= 8
4
```

別の方法としては、戻り値はタプルのため、パラメータ番号を指定して取得できます。

```python
a = func1(10)[1]
print(a)
```

```
結果
IN  x= 10 a= 2 b= 3 c= 4
OUT x= 20 a= 4 b= 6 c= 8
4
```

## リストを利用した受け取り方
リストは、残りのパラメータをすべて取得する場合に利用します。
$*$をつけます。

```python
x, *oparam = func1(10)
print(x)
print(oparam)
```

```
結果
IN  x= 10 a= 2 b= 3 c= 4
OUT x= 20 a= 4 b= 6 c= 8
20
[4, 6, 8]
```

## 辞書を利用した受け取り方
戻り値に名前を付けて返却する方法が分かりませんでした。
戻り値自体を辞書にしてみました。

```python
def func1d(x, a=2, b=3, c=4):
    print("IN  x=", x, "a=", a, "b=", b, "c=", c)
    x *= 2
    a *= 2
    b *= 2
    c *= 2
    print("OUT x=", x, "a=", a, "b=", b, "c=", c)
    return x, {"a":a, "b":b, "c":c}
```

戻り値を確認してみます。

```python
x, oparam = func1d(10)
print(x)
print(oparam)
```

```
結果
IN  x= 10 a= 2 b= 3 c= 4
OUT x= 20 a= 4 b= 6 c= 8
20
{'a': 4, 'b': 6, 'c': 8}
```

# 異なるパラメータ、戻り値の関数の同一インタフェースによる呼び出し
パラメータ、戻り値の異なる関数を同一インタフェースで呼び出すことを考えます。
以下の２つの関数で考えます。

```python
def func1(x, a=2, b=3, c=4):
    print("IN  x=", x, "a=", a, "b=", b, "c=", c)
    x *= 2
    a *= 2
    b *= 2
    c *= 2
    print("OUT x=", x, "a=", a, "b=", b, "c=", c)
    return x, a, b, c
def func2(x, a=2, d=5):
    print("IN  x=", x, "a=", a, "d=", d)
    x *= 3
    a *= 3
    d *= 3
    print("OUT x=", x, "a=", a, "d=", d)
    return x, a, d
```

## パラメータ位置固定
パラメータ位置を固定できる場合の呼び出しは、リストを利用して行います。

```python
def call_funcl(func, x, *param):
    x, *param = func(x, *param)
    return x, param
```

func1, func2両方の関数を呼び出してみます。

```python
x, param = call_funcl(func1, 10, 20, 30)
print(x)
print(param)
```

```
結果
IN  x= 10 a= 20 b= 30 c= 4
OUT x= 20 a= 40 b= 60 c= 8
20
[40, 60, 8]
```

```python
x, param = call_funcl(func2, 10, 20, 30)
print(x)
print(param)
```

```
結果
IN  x= 10 a= 20 d= 30
OUT x= 30 a= 60 d= 90
30
[60, 90]
```

両方の関数が呼び出せました。

３回連続で呼び出してみます。

```python
x, param = call_funcl(func1, 10, 20, 30)
x, param = call_funcl(func1, x, *param)
x, param = call_funcl(func1, x, *param)
```

```
結果
IN  x= 10 a= 20 b= 30 c= 4
OUT x= 20 a= 40 b= 60 c= 8
IN  x= 20 a= 40 b= 60 c= 8
OUT x= 40 a= 80 b= 120 c= 16
IN  x= 40 a= 80 b= 120 c= 16
OUT x= 80 a= 160 b= 240 c= 32
```

## パラメータ名指定
辞書を利用します。
以下の２つの関数で考えます。

```python
def func1d(x, a=2, b=3, c=4):
    print("IN  x=", x, "a=", a, "b=", b, "c=", c)
    x *= 2
    a *= 2
    b *= 2
    c *= 2
    print("OUT x=", x, "a=", a, "b=", b, "c=", c)
    return x, {"a":a, "b":b, "c":c}
def func2d(x, a=2, d=5):
    print("IN  x=", x, "a=", a, "d=", d)
    x *= 3
    a *= 3
    d *= 3
    print("OUT x=", x, "a=", a, "d=", d)
    return x, {"a":a, "d":d}
```

２つの関数を呼び出す関数を考えます。

```python
def call_funcd(func, x, **param):
    x, param = func(x, **param)
    return x, param
```

実際に呼び出してみます。

```python
x, param = call_funcd(func1d, 10, a=20, c=40)
print(x)
print(param)
```

```
結果
IN  x= 10 a= 20 b= 3 c= 40
OUT x= 20 a= 40 b= 6 c= 80
20
{'a': 40, 'b': 6, 'c': 80}
```

```python
x, param = call_funcd(func2d, 10, d=50)
print(x)
print(param)
```

```
結果
IN  x= 10 a= 2 d= 50
OUT x= 30 a= 6 d= 150
30
{'a': 6, 'd': 150}
```

両方呼び出せました。

３回連続して呼び出してみます。

```python
x, param = call_funcd(func1d, 10, a=20, c=40)
x, param = call_funcd(func1d, x, **param)
x, param = call_funcd(func1d, x, **param)
```

```
結果
IN  x= 10 a= 20 b= 3 c= 40
OUT x= 20 a= 40 b= 6 c= 80
IN  x= 20 a= 40 b= 6 c= 80
OUT x= 40 a= 80 b= 12 c= 160
IN  x= 40 a= 80 b= 12 c= 160
OUT x= 80 a= 160 b= 24 c= 320
```

なかなかおもしろいですね。
パラメータを順序で受け取る場合は、リストを、名前付きで受け取る場合は、辞書を利用すること、関数の戻り値はタプルであることを覚えておけば、迷わずに対応できます。