LoginSignup
6
7

More than 5 years have passed since last update.

参照渡しと値渡し

Last updated at Posted at 2018-09-10

参照渡しと値渡し

go言語を勉強し始めてから久しぶりにポインタを利用することになったので改めてポインタやらアドレスやらのややこしいとこを自分なりに見直しました.

値渡し

go言語の場合

person.go
package main

import "fmt"

type Person struct {
    Name string
    age int
}

func growUp(person Person) {
    person.age++
    fmt.Println("innerFunc", person.age)
}

func main() {
    var person Person
    person.Name = "とくまる"
    person.age = 20
    fmt.Println("before", person.age)
    growUp(person) // 1コ歳をとらせる
    fmt.Println("before", person.age)
}

このプログラムを実行してみます

% go run person.go
before 20 // 関数実行前
innerFunc 21 // 関数内での出力
after 20 // 関数実行後のmain()内での出力

出力結果は上記のようになり,関数内では年齢が1コ増えてますが,afterの出力を見てわかるように関数に渡したpersonの値は変わっていません!これが値渡しで,personのコピーを作って関数に渡しているような感じです

参照渡し

次に参照渡し

person.go
package main

import (
    "fmt"
)

type Person struct {
    Name string
    age int
}

func growUp(person Person) {
    person.age++
    fmt.Println("innerFunc:", person.age)
}

//変更点/////////////////////////////////
func fixGrowUp(person *Person) {
    person.age++
    fmt.Println("innerFunc:", person.age)
}
/////////////////////////////////////////


func main() {
    //var person Person
    person := &Person{}
    person.Name = "とくまる"
    person.age = 20
    fmt.Println("before:", person.age)
    //growUp(person)
    fixGrowUp(person)
    fmt.Println("after:", person.age)
}

新しくfixGrowUpという関数を作りました.出力結果は以下のようになります.

% go run person.go
before: 20 // 関数実行前
innerFunc: 21 // 関数内での出力
after: 21 // 関数実行後のmain()内での出力

今度は関数実行後の出力も歳をとっています.これが参照渡しです.アドレスのポインタ渡して,関数に渡したpersonを書き換えてます.

いや

知ってたでって感じの人が多いと思いますが,C言語に挫折し,pythonやらjavascriptやらでポインタとおさらばしてなんとなくプログラミングしてたらすっかり気にしなくなってました。(笑)

Pythonでは?

ここからが本題です!

前置きが長くなりましたが,伝えたかったのはここからです.参照渡しとかほとんど意識せずプログラム書いてたけどpythonだとどうなってるの?って思って調べてみました.

pythonは全て参照渡し

らしいです.そして,書き換わる型と書き変わらない型があるらしいです.以下を見てください.

person.py
def change_int(age):
    age = 21

def main():
    age = 20
    data = {"name": "とくまる", "age": 20}
        print("type", type(age))
        print("before: ", age)
        change_int(age)
    print("after: ", age)

main()
% python person.py
type <class 'int'>
before:  20
innerFunc 20
after:  21

これは,int型のageを書き換えたものですがpythonは全て参照渡ししてるのに書き換わってないですね.参照渡しするけど「書き換えるならコピーして元のと別のやつ書き換えるよ」みたいな?次にdict型

person.py
def change_int(age):
    age = 21
    print("innerFunc", age)

################変更点###########################
def change_dict(person):
    person["age"] = 21
    print("innerFunc", person["age"])
##################################################
def main():
    age = 20
    person = {"name": "とくまる", "age": 20}
    print("type", type(person))
    print("before: ", person["age"])
    # change_int(age)
    change_dict(person)
    print("after: ", person["age"])

main()

今度はdict型の中の変数を書き換える関数を書きました.出力結果は以下のようになります.

% python person.py
type <class 'dict'>
before:  20
innerFunc 21
after:  21

書き換わってますね.dict型は書き換えちゃうみたいですね.書き換わるやつ(mutable)と書き変わらんやつ(immutable)は以下のようになっています.

  • 書き換えできない型
    • int, float, str, tuple, bytes, frozenset ..
  • 書き換え可能な型
    • list, dict, set, bytearray ..

なんとなくpythonが簡単だからと思ってプログラミングしてる方は以上のことを意識してプログラミングしていきましょう!

おまけ

listがmutableだから


def implement(awesome_list):
    for elem in awesome_list:
        elem += 1
def main():
    awesome_list = [1, 2, 3] # intのリスト
    implement(awesome_list)
    print(awesome_list)

上記のようにして書き換えたくなりますが,ここで生成されるelemawesome_listの要素のコピーなので,

    elem += 1

では要素のコピーを書き換えてることになります.したがって,main()での出力結果は1, 2, 3のままです.解決方法としては

  1. 関数内で新しい空のリストを定義して,for文の中でappendする
  2. リストの要素に直接アクセスする
    for i in range(len(awesome_list)):
        awesome_list[i] += 1

まあざっくりと,(1)「新しいリスト作って書き換えた要素追加する」か,(2)「リストの要素の番号指定してちまちまアクセスして書き換えてく」かって感じですかね個人的に(2)だとpythonのよさみたいなものが消えてるような気がするので(1)をおすすめします

* ここではリストの要素がint型(immutable)なので書き換えれませんでしたが,リストの要素がmutableの場合は書き換え可能です!

新しい言語を始める際のチェックポイントとして「値渡しと参照渡し」のルールについてその言語ではどうなってるのかっていうのはチェックしておくとスムーズ正確にプログラミングできると思います!

6
7
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
7