今さらPython3 (27) - 引数

この本の第4章、関数のあたりを読み進めています。

入門 Python 3

入門 Python 3

名前指定ありとなし

>>> def menu(wine, entree, dessert):
...     return {'wine': wine, 'entree': entree, 'dessert':dessert}
... 
>>> menu('chardonnay', 'chiken', 'cake')
{'wine': 'chardonnay', 'dessert': 'cake', 'entree': 'chiken'}
>>> menu('beef', 'bagel', 'bordeaux')
{'wine': 'beef', 'dessert': 'bordeaux', 'entree': 'bagel'}
>>> 

位置引数という言葉は、正直初めて聞いたんだけど、引数の順番に合わせて値を受け渡すいわば名前指定なしって感じか。

>>> menu(entree='beef', dessert='bagel', wine='bordeux')
{'wine': 'bordeux', 'dessert': 'bagel', 'entree': 'beef'}
>>> 

キーワード引数という言葉も初めて聞いた。ただ、引数名=値で値を受け渡すだけなので、難しい話じゃない。

>>> menu('frontenac', dessert='flan', entree='fish')
{'wine': 'frontenac', 'dessert': 'flan', 'entree': 'fish'}
>>> menu(dessert='flan', 'frontenac', entree='fish')
  File "<stdin>", line 1
SyntaxError: non-keyword arg after keyword arg
>>> 

位置引数とキーワード引数は混在できるけど、その場合は位置引数を前に持ってこないとダメと。

>>> menu('fran', wine='frontenac', entree='fish')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: menu() got multiple values for argument 'wine'
>>> 

だめ押しでもう1回やってみた。wineが2回指定されていると怒られた。ちょっと考えれば当たり前か。位置引数は何番目の位置にあるかで、どの引数に対する値かを判別しているわけだから。

引数にデフォルト値を与える

>>> def menu(wine, entree, dessert='pudding'):
...     return {'wine': wine, 'entree': entree, 'dessert':dessert}
... 
>>> menu('chardonnay', 'chiken')
{'wine': 'chardonnay', 'dessert': 'pudding', 'entree': 'chiken'}
>>> menu('dunkelfelder', 'duck', 'doughnut')
{'wine': 'dunkelfelder', 'dessert': 'doughnut', 'entree': 'duck'}
>>> 

上の例では、dessertに'pudding'という初期値を与えているので、関数を呼ぶときにdessertに値を与えないで実行した場合は、初期値が受け渡される。

>>> def buggy(arg, result=[]):
...     result.append(arg)
...     print(result)
... 
>>> buggy('a')
['a']
>>> buggy('b')
['a', 'b']

自分が普段仕事で使っている某言語ではちょっと考えられないけど、これが「デフォルト引数が計算されるのは、関数が実行されたときではなく、定義されたときだ」ということかな。

>>> def works(arg):
...     result = []
...     result.append(arg)
...     return result
... 
>>> works('a')
['a']
>>> works('b')
['b']
>>> 
>>> def nonbuggy(arg, result=None):
...     if result is None:
...         result = []
...     result.append(arg)
...     print(result)
... 
>>> nonbuggy('a')
['a']
>>> nonbuggy('b')
['b']
>>> 

1つ目は関数の中でresult=[]をやって初期化している。2つめはresultのデフォルト値をNoneにしていて、デフォルト値のまま受け渡されたら初期化して、引数としてもらったらappendで追加していると理解できる。

>>> a = nonbuggy('a')
['a']
>>> nonbuggy('b', a)
['b']

じゃ、1回目の結果をaという変数にもらって、それを2回目の時に渡したら['a', 'b']になると予想したけどなんか変だな?

>>> a
>>> 

aが空っぽになっている。そっか、さっきの関数にreturnがないからだ。

>>> def nonbuggy(arg, result=None):
...     if result is None:
...         result = []
...     result.append(arg)
...     print(result)
...     return result
... 
>>> nonbuggy('a')
['a']
['a']
>>> ar = nonbuggy('a')
['a']
>>> nonbuggy('b', ar)
['a', 'b']
['a', 'b']
>>> 

なるほど。これで期待していた動きになった。もちろん関数を変更する前のヤツでも、こうやって実行すればOKだったはず。

>>> nonbuggy('b', ['a'])