見出し画像

引数の受け渡し、ミュータブルとイミュータブルの違いとは?

pythonには、ミュータブル(変更可能)とイミュータブル(変更不可)という概念があります。
引数を受け渡す際の両者の違いについて、見ていきたいと思います。

サンプル1(イミュータブルの例)

def main():
    result = True
    submain(result)
    return result

def submain(result):
    result = False


result = main()
print(result)

これは、result = main()で、main()関数からreturnで返されるresultの値を取得し、その値をprintで出力するプログラムとなっています。

main()関数では、resultの値はTrueとなり、submain()関数が実行されます。

submain()関数内でresultの値はFalseとなりますが、これはsubmain()関数内だけのローカル変数のため、main()関数内のresultには全く影響を及ぼしません。
resultは、それぞれの関数内のローカル変数であり、returnで返される値はあくまでもmain()関数内の値となります。

その結果、最終的にはprint(result)Trueが出力されます。


サンプル2(ミュータブルの例)

def main():
    result = [True]
    submain(result)
    return result[0]

def submain(result):
    result[0] = False


result = main()
print(result)

これは、サンプル1とほぼ同じプログラムですが、main()関数のresultがリスト形式のデータとなっており、Trueという値を持っています。
そして、submain(result)で、resultのリストをsubmain()関数へ受け渡しています。

submain()関数では、result[0] = Falseで、リストの0番目の要素すなわちTrueFalseに書き換えています。
リストは、ミュータブル(変更可能)な変数であるため、関数を跨いで変更を加えることができるのです。

submain()関数の処理が終わり、main()関数に戻って、returnresult[0]すなわち、リストの0番目の要素を返しています。
その結果、最終的にはprint(result)Falseが出力されます。


イミュータブル(変更不可)とミュータブル(変更可能)

サンプル1のresultは、イミュータブル(変更不可)であるため、main()関数とsubmain()関数におけるresultは、それぞれ関数内のローカル変数であり無関係の存在です。

一方、サンプル2のresultは、ミュータブル(変更可能)であるため、他の関数へ受け渡して他の関数内での変更されると、呼び出し元に反映される形になります。

この結果、サンプル1はTrue、サンプル2はFalseと出力されることとなりました。

ミュータブル型には、リストや辞書などがあります。一方、数値や文字列、小数、バイナリデータ、タプル、論理(True / False)などはイミュータブル型です。


イミュータブル型でも異なる関数へ受け渡して値を変更したい場合

イミュータブル型を異なる関数へ受け渡し、値を変更して反映したい場合、以下のようにクラス(class)を使用すれば可能です。

クラスとは、ここでは、複数の関数をまとめたかたまりと理解してください。

class Main:
    def __init__(self):
        self.result = True
        Main.submain(self)

    def submain(self):
        self.result = False


result = Main().result
print(result)

ここでは、result = Main().resultで、Mainクラスを実行しMainクラスのresultの値を取得し、printするプログラムになっています。

def __init__(self):とは、Mainクラスを実行したときに、最初に実行される処理をまとめたものです。ここでは、selfという引数を使用し、self.result = Trueとしています。

self.resultとインスタンス化することにより、クラス内の複数の関数でselfを受け渡すことで、resultはローカル変数ではなくなり、自由に値を変更できる変数(インスタンス変数)となります。

Main.submain(self)により、Mainクラスのsubmain()関数が実行され、self.resultの値はFalseに書き換えられます。

Main().resultとすると、Mainクラスでインスタンス化されたresultという変数の値が呼び出されます。Mainクラスのself.resultの値がresultに代入されることになります。
その結果、最終的にはprint(result)で、Falseが出力されます。





いいなと思ったら応援しよう!