こんにちは。PyQチームのtsutomuです。
今日は、「副作用のない関数を副作用があるように変更する方法」を紹介します。
関数の引数のオブジェクトが、関数実行後に変化していると「副作用があった」ことになります。
質問
下記のremove_firstを実行後に、引数のリストの先頭が削除されるようにするには、どのようにしたら良いですか?
書いたコード
def remove_first(lst_): lst_ = lst_[1:] print(lst_)
回答
下記のように副作用があるコードであれば、仮引数を通して実引数の中身を変えることも可能です。
def remove_first(lst_): if lst_: del lst_[0] # 先頭の要素を削除 print(lst_) lst = [1, 2, 3, 4] print(lst) remove_first(lst) remove_first(lst)
副作用のない関数
PyQのクエスト「仮引数の使い方を学ぼう2問目」では、以下のコードを扱いました。
def remove_first(lst_): lst_ = lst_[1:] print(lst_) lst = [1, 2, 3, 4] print(lst) # [1, 2, 3, 4] remove_first(lst) # [2, 3, 4] remove_first(lst) # [2, 3, 4] print(lst) # [1, 2, 3, 4]
※ クエストでは実引数と仮引数の名前は同じですが、ここでは区別しやすいように、実引数(lst
)とは別に仮引数の名前をlst_
にしています。
remove_first
を何回実行しても、 lst
は変更されず、出力も[2, 3, 4]
のままです。
これは、remove_first
内で変数lst_
に新しい値lst_[1:]
を代入しているためです。
これにより、変数lst_
は、元のlst_
とは別のオブジェクトになるので、実引数のlst
は影響を受けません。
副作用のある関数
先ほどの関数remove_first
を以下のように変えてみましょう。
def remove_first(lst_): if lst_: del lst_[0] # 先頭の要素を削除 print(lst_) lst = [1, 2, 3, 4] print(lst) # [1, 2, 3, 4] remove_first(lst) # [2, 3, 4] remove_first(lst) # [3, 4] print(lst) # [3, 4]
実引数であるlst
が、関数remove_first
の中で、変更されたことになります。
このように、del lst_[0]
とすることで、変数lst
の中身を変更できます。
仮引数とは実引数に別名をつけた変数であり、今回示したlst
のようにミュータブルであれば、仮引数を通して実引数の中身を変えることも可能です。
参考
- ミュータブル:変更可能なオブジェクト(リストや辞書など)
- イミュータブル:変更不可能なオブジェクト(整数や文字列など)
関連するクエスト
このお悩み解決はPyQのクエスト「仮引数の使い方を学ぼう」の2問目の補足情報として作成しています。