質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

4回答

1545閲覧

参照渡しと実値渡しがよくわからない

h5x

総合スコア11

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

0グッド

1クリップ

投稿2020/10/11 12:03

Python

1x = 10 2a = x 3b = x 4x = 1 5print (a) 6print (b) 7print (x)

数字については例外なくすべて実値渡しでしょうか?

Python

1x = [1,2] 2a = x 3b = x 4x = [1,2,1] 5print (a) 6print (b) 7print (x) 8print () 9 10x = [1,2] 11a = x 12b = x 13x.append(1) 14print (a) 15print (b) 16print (x)

配列についてはどちらも同じ結果になると思ったのですが、実際には配列渡しになったり参照渡しとなりxが勝手に変更されたりよくわからない挙動をします。
どういったときにどのような挙動をするのか使いこなせるようになるには、どのように勉強すればいいでしょうか?
参照渡しになったり実値渡しになったりする言語は初めてでかなり戸惑っています。
宜しくお願い致します。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

otn

2020/10/11 13:23

そもそも、「値渡し」「参照渡し」というのは、関数やメソッドの引き数引き渡しの仕組みの用語であるというのは理解されていますか?
h5x

2020/10/11 13:58

他の言語(Perl)で色々試行錯誤していたらいつの間にかプログラムが組めるようになった感じで体系だって勉強したことがなく用語についてはあまり理解できていません。すみません。 今回の場合、変数の中身ではなく、変数そのものを示しており(メモリのアドレス?)、参照渡しかと思った感じです。
otn

2020/10/11 14:03

関数やメソッドの引き数引き渡しの仕組み以外の場面で、「値渡し」「参照渡し」という用語を使ったのが、混乱の元の一つだと思います。使うのを止めてみましょう。
h5x

2020/10/11 14:28

その用語を使うのを辞めるのは良いのですが、適切が語彙がわからず困っています。多言語からのPython入門者が違いを質問する場合、何と書けば伝わりますでしょうか?
otn

2020/10/11 14:32

ありのままに書けばいいかと。
juner

2024/04/10 06:36

参照渡し ではなく 参照の値渡し もしくは 共有渡し でいいのではないでしょうか?(ニュアンス的に
guest

回答4

0

参照渡しになったり実値渡しになったりする言語は初めてでかなり戸惑っています。

Pythonに参照渡しは存在しません

前提として、Python では引数は代入によって渡されます。代入はオブジェクトへの参照を作るだけなので、呼び出し元と呼び出し先にある引数名の間にエイリアスはありませんし、参照渡しそれ自体はありません。

また、そもそも代入を○○渡しと呼ぶこともありません。


変数への代入が、他の変数に影響することはありません。

####リストの場合

Python

1>>> a = [1] 2>>> b = a 3>>> print(a, b) 4[1] [1] 5>>> a = [2] 6>>> print(a, b) 7[2] [1]

####数値の場合

Python

1>>> a = 1 2>>> b = a 3>>> print(a, b) 41 1 5>>> a = 2 6>>> print(a, b) 72 1

変数の参照先への操作は、同じオブジェクトを参照している他の変数にも影響します。

####リストの場合

Python

1>>> a = [1] 2>>> b = a 3>>> print(a, b) 4[1] [1] 5>>> a.append(2) 6>>> print(a, b) 7[1, 2] [1, 2]

####数値の場合
Pythonの数値はイミュータブルなので、直接書き換える方法が存在しません
これはオブジェクトの保持の仕方とは全く関係ありません。

他の言語では?

参照渡しになったり実値渡しになったりする言語は初めてでかなり戸惑っています。

過去質問を見る限り、JavaScriptとPerlのご経験があるようですね。

JavaScript

1var x = [1,2]; 2var a = x; 3var b = x; 4var x = [1,2,1]; 5console.log(a); // [ 1, 2 ] 6console.log(b); // [ 1, 2 ] 7console.log(x); // [ 1, 2, 1 ] 8console.log(); 9 10var x = [1,2]; 11var a = x; 12var b = x; 13x.push(1); 14console.log(a); // [ 1, 2, 1 ] 15console.log(b); // [ 1, 2, 1 ] 16console.log(x); // [ 1, 2, 1 ] 17console.log();

Perl

1$x = [1, 2]; 2$a = $x; 3$b = $x; 4$x = [1, 2, 1]; 5print "@$a\n"; # 1 2 6print "@$b\n"; # 1 2 7print "@$x\n"; # 1 2 1 8print "\n"; 9 10$x = [1, 2]; 11$a = $x; 12$b = $x; 13push @$x, 1; 14print "@$a\n"; # 1 2 1 15print "@$b\n"; # 1 2 1 16print "@$x\n"; # 1 2 1 17print "\n";

JavaScriptはあまり詳しくないですし、
Perlに至ってはさっきまでHello Worldすら書いたことがありませんでした。

変な間違いをしていたらすみません。

投稿2020/10/11 12:13

編集2020/10/11 13:34
LouiS0616

総合スコア35668

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

h5x

2020/10/11 13:52

ありがとうございます。 javascriptはHelloWorldも今では忘れてしまっているためPerlにて失礼します。 「Pythonに参照渡しは存在しません。」とのことですが、用語について取り間違えていたらすみませんが、寧ろ、参照渡しのような気がしたのです。 以下の例ですと、 Python x = [1,2] a = x b = x x = [1,2,1] print (a)#[1,2,1] Perlであればxの中身が書き変わろうと一度内容をコピーした以上影響しないはず print (b)#[1,2,1] Perlであればxの中身が書き変わろうと一度内容をコピーした以上影響しないはず print (x)#[1,2,1] Perlだとこんな感じをイメージしていました。 # @で始まる変数はPerlでは配列扱いとなる @x = (1, 2); @a = @x;#ここで@xの中身の実値データをコピー。その為、のちほど@xの中身が変化しても@aや@bは変化しない @b = @x;#上記に同様 @x = (1, 2, 1); print "@a\n"; # 1 2 print "@b\n"; # 1 2 print "@x\n"; # 1 2 1 print "\n"; Perlではよく、 @source_data = &GetHTML($url); @temp_source_data = @source_data; このようにして @temp_source_dataに@source_dataのコピーを作成しそこで様々な処理をしていました。 加工前のデータがほしいときには、 @temp_source_data2 = @source_data; とすれば、@source_dataからはいつでも加工前のデータが得られるため重宝していたのです。 今回、このような使い方をするとトラブルが出て驚いた感じです。 冗長になりますが、実際に失敗したPythonコードはこんな感じです。(実際には1つの一時変数でもできますが当初このコードで失敗しています) Python 配列の中身を回そうと思っていたのですが、実際には配列の個数が減っていき失敗しました。 s = [] with open("list.txt") as f: s = f.read() s = re.split("\n",s) s.pop() temp = s#sに影響を与えたくないため一時ファイルを作成 temp2 = s for num,x in enumerate(s): t = temp.pop(0) t2 = temp2.pop() temp2.append(t) Excute( temp2 )
LouiS0616

2020/10/11 14:03 編集

Pythonの変数は、実は値を直接は保持していません。 値がどこにあるかを束縛しています。数値でもリストでも、どんな値もです。 Cで言うところのポインタ、(たぶん)Perlで言うところのリファレンスに相当します。 値を実際に持ちまわした方が意味論的には分かり易いのですが、フルコピーが何度も生じかねずメモリを圧迫します。また実装側から見てもメモリ確保が面倒です。 JavaやPythonをはじめ、参照の持ちまわしをシステム内に隠ぺいしている言語は多いです。 しかし、これを参照渡しとは呼びません。
LouiS0616

2020/10/11 14:11 編集

言葉の用法はさておき。 少なくともPythonでは、値の種類に依って代入時の動作が変わることはありません。 ネットで調べると『イミュータブルは値渡し、ミュータブルは参照渡し』みたいな説明をしているサイトもちらほら見かけますが、これは完全に誤りです。
h5x

2020/10/11 14:25

ありがとうございます。 用語について正直なんとなくしか理解ができなかったです。 Arduinoのようなマイコンでは困ったことがありますが、最近のPCは速くメモリも大きいので、動くコードをだらだら書く事が多いですが、どのような方法がありますか? 用語について、何と呼べばいいのか分かりませんが、ネットであるいわゆる、値渡しと参照渡しの使い分けができずハマりそうで困っています。 deepcopyを常にすればとりあえずは動くコードが書けるかと思ったのですがハマりポイントなどありますでしょうか?
maisumakun

2020/10/11 23:52

> Perlであればxの中身が書き変わろうと一度内容をコピーした以上影響しないはず Pythonではリストは値型ではなく参照型です。変数代入でコピーはされません。
LouiS0616

2020/10/12 09:14

@h5x さん deepcopyを多用しても案外困らないのかも知れないですが、言語には言語ごとの特色だとか設計思想がありますから、各言語の挙動に慣れるのが最も良いように思います。 > ネットであるいわゆる、値渡しと参照渡しの使い分けができずハマりそうで困っています。 1. 値の代入をことを○○渡しとは呼びません。 2. 参照渡しが存在する言語は多くないです(有名どころだとC++、C#、PHP、VBあたり)。それ以外の言語について『参照渡し』という単語を用いるのは推奨しません。 少なくともPythonについては、使い分けを意識する必要がありません。 そもそも値渡ししか存在しないからです。
guest

0

「再代入」と「破壊的変更」を、きちんと区別しましょう。

  • 再代入…「変数に何が入っているか」というつながりを変更する
  • 破壊的変更…「変数に何が入っているか」はそのままだけど、入っているものの方を加工する

「3」という整数の性質を変えることが想定できないように、数値の場合は変更はすべて再代入によります。一方で、x.append(1)とするのは、入っているリストそのものを変更する破壊的変更の操作です。

投稿2020/10/11 12:12

maisumakun

総合スコア146018

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2020/10/11 12:13

x = [1,2,1]とするのは、ここで新しいリストを作ってxに代入しているだけなので、前のリストが入ったaやbには無関係な話です。
guest

0

(落ち穂拾い的回答です。maisumakunさんの回答に賛成ですが、"変数に入っている"という表現に違和感があったのでそこの指摘といった感じです)

質問者さんが本当に理解できてない急所的なポイントは、実は「代入文とはなにか? 変数とは何か?」だと感じました。

Pythonはここに癖がある言語だと思います。

https://docs.python.org/ja/3/reference/simple_stmts.html#assignment-statements

代入文は、名前を値に (再) 束縛したり、変更可能なオブジェクトの属性や要素を変更したりするために使われます

代入文は

  1. 名前を値に (再) 束縛する
  2. 変更可能なオブジェクトの属性や要素を変更する

ものだと明記されています。代入文の説明に"変数"や"代入"という用語が出てこないことは象徴的です。
"代入文"という言語要素はあるくせにその機能に"変数への代入操作"がありません。

言語仕様のページに"変数"という項目がないことも象徴的です。
実のところ、Pythonの実行モデルには変数がありません。
あるのは"名前づけと束縛"なのです。

https://docs.python.org/ja/3/reference/executionmodel.html#naming-and-binding

名前 (name) は、オブジェクトを参照します。名前を導入するには、名前への束縛 (name binding) 操作を行います。
(略)
ある名前がブロック内で束縛されているなら、 nonlocal や global として宣言されていない限り、それはそのブロックのローカル変数 (local variable) です。 ある名前がモジュールレベルで束縛されているなら、その名前はグローバル変数 (global variable) です。

この辺りの表現、"名前はオブジェクトを参照します。ある名前がブロック内で束縛されているなら、その名前はそのブロックのローカル変数です"といった文が腑に落ちるとよいだろうなと思いました。


あと、質問本体に関して確認/検索するべき用語はミュータブル/イミュータブルです(変更可能/変更不能or不変 とも言いますが、検索しやすさからミュータブル/イミュータブルを好んで使うプログラマが多いはず)。

https://docs.python.org/ja/3/glossary.html#term-mutable

ミュータブルなオブジェクトは、 id() を変えることなく値を変更できます。

https://docs.python.org/ja/3/glossary.html#term-immutable

固定の値を持ったオブジェクトです。イミュータブルなオブジェクトには、数値、文字列、およびタプルなどがあります。これらのオブジェクトは値を変えられません。別の値を記憶させる際には、新たなオブジェクトを作成しなければなりません。

https://docs.python.org/ja/3/reference/datamodel.html#index-15

このあたりでしょう。

生成したあとに中身を変えられるオブジェクトか、中身を変えられないオブジェクトか? という違いがあるという話です。


すでに複数の指摘は出ていますが、値渡しと参照渡しは関数の呼び出し時に引数をどうやって評価するか? ということに関する用語で、かつPythonには値渡ししかありません

値渡しと参照渡しの違いは、言語仕様に値渡しと参照渡しの両方を含む言語を勉強するのが早いのでしょう。

投稿2020/10/11 23:48

編集2020/10/11 23:50
quickquip

総合スコア11235

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

回答としては不適切かもしれませんが、極力、参照渡しにならない方法を使うのがよいと個人的には思っています。

例えば、変数にリストを入れたい場合は、

Python

1num_list = [1, 2, 3, 4, 5] 2 3a = num_list[:]

のようにして、値渡しになるようにします。

イメージ説明

投稿2020/10/11 13:24

takutakuya

総合スコア979

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

LouiS0616

2020/10/11 13:41 編集

※低評価したのは私ではありません 結構ハマりやすいですよね。 >>> a = [[1, 2], [4, 5]] >>> b = a[:] >>> print(a, b) [[1, 2], [4, 5]] [[1, 2], [4, 5]] >>> >>> a.append([7, 8]) >>> print(a, b) [[1, 2], [4, 5], [7, 8]] [[1, 2], [4, 5]] >>> >>> a[0].append(3) >>> print(a, b) [[1, 2, 3], [4, 5], [7, 8]] [[1, 2, 3], [4, 5]]
h5x

2020/10/11 14:06

ありがとうございます。 takutakuyaさん わたしもそのような使い方ができるのであれば良いと思っています。ググった範囲では、ミュータブルとイミュータブルがあり、更にその中でも条件により異る場合があるようで、正直複雑で覚えきれず、今は全部deepcopyをするのが間違いにくいコードになるかと思ったりもしています。 LouiS0616さん 完全にコピーできないのですね。ハマる前に教えてくださりありがとうございます!
takutakuya

2020/10/11 14:18

ん〜、まぁいろんなケースがあるので、想定外のエラーがあった時は参照渡しを疑えれば良いと思いますけどね。 匿名で低評価できるこのシステム、嫌いです。。。
hentaiman

2020/10/11 14:20

はい 低評価しましたよ これでいいですか? 質問に対してせめて(間違っているかも)という補足でもつけておけばと思いますね。よほど難しい問題なら仕方ないかもしれませんが。
takutakuya

2020/10/11 14:23

hentaiman さん 名乗り出ていただいき、ありがとうございます。 指摘いただいた点、今後に生かしていきます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問