この記事は 「Pythonって、たぶんそういう意味じゃない」シリーズ の第7弾です。
Python歴5年以上、メインで使い続けてきた変態が書いています。
この記事は 個人の経験に基づくポエムです。
環境・用途・経験によって異なる場合があります。
コメントで教えてもらうまで、知らなかった。
Pythonを5年以上使ってきて、defの本当の意味を
意識したことが一度もなかった。
Scratchで育ったから「上から実行」は当たり前だった
Scratchをやっていた。
ブロックは上から順番に実行される。
それが「プログラムの動き方」として最初に染み付いた。
Pythonに移ったときも、その感覚は自然に引き継がれた。
「上から順番に実行される」は、自分にとって当たり前すぎて意識したことすらなかった。
defは「宣言」じゃなくて「実行文」だった
コメントで初めて知った。
Pythonのdef文は、関数を宣言しているのではない。
関数オブジェクトを生成する実行文だ。
つまりPythonがdefの行を通ったとき、
その瞬間に関数オブジェクトが作られる。
これが何を意味するか。デフォルト引数の話につながる。
知らないとハマるコード
def add_item(item, items=[]):
items.append(item)
return items
print(add_item("a")) # ["a"]
print(add_item("b")) # ["a", "b"] ← !?
print(add_item("c")) # ["a", "b", "c"] ← !?
毎回["a"]、["b"]、["c"]が返ってくると思うじゃないですか。
でもdef文が実行されたその瞬間に[]が一度だけ作られる。
呼び出すたびに同じリストを使い回してしまう。
「毎回新しいリストが作られる」は間違い。
デフォルト引数のオブジェクトは関数定義時に一度だけ生成される。
正しい書き方
def add_item(item, items=None):
if items is None:
items = []
items.append(item)
return items
Noneをデフォルトにして、関数の中で毎回新しいリストを作る。
これが定石だ。
defが実行文だと知っているだけで、この書き方の意味が腑に落ちる。
知らないと「なんでNoneにするんだろう」で終わってしまう。
知らなくても5年使えた。でも知ってたら違った
自分はこの罠にハマったことがない。
必要な場面がなかっただけかもしれないし、たまたま運が良かっただけかもしれない。
でもコメントで教えてもらって思った。
知っているかどうかで、コードの読み方が変わる。
defを見たとき、「宣言している」と思うか「実行している」と思うかで、
Pythonの動きへの理解が全然違う。
-
defは実行文。通過した瞬間に関数オブジェクトが生成される - デフォルト引数は定義時に一度だけ評価される
- ミュータブルなデフォルト引数は使い回されるので注意
「知らなくても書ける」と「知って書く」は、たぶんそういう意味じゃない。
「Pythonって、たぶんそういう意味じゃない」 シリーズでは、
こういう"なんとなくで語られがちなPython"を言語化していきます。
👉 ストックをフォローしておくと次の記事を見逃しません!