Python学習チャンネル by PyQ

Pythonのオンライン学習プラットフォームPyQのオフィシャルブログです

副作用のない関数を副作用があるように変更するには

f:id:kenken0326:20210219160618p:plain

こんにちは。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問目の補足情報として作成しています。

pyq.jp

Copyright ©2017- BeProud Inc. All rights reserved.