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

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

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

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

Q&A

解決済

5回答

2111閲覧

関数の引数を直接書き換える意義

MagMag

総合スコア80

Python 3.x

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

0グッド

1クリップ

投稿2019/02/11 00:26

ある教科書に記載されていたコードを実行していたのですが、以下コードのfunc_1ように、返り値をreturnで指定しないで、引数をそのまま変更するようなスクリプトなっていました。そこで、以下2点教えていただけないでしょうか?

・可読性が悪いので、returnで引数を明示する方がベターかと思いますが、このコード(func_1)の利点はありますでしょうか?
・func_1では値が上書きされるのに、func_2ではなぜ値が上書きされないのでしょうか?

Python

1def func_1(x): 2 x[0]=3 3 return 4 5def func_2(x): 6 x = 3 7 return 8 9a=[1,1] 10func_1(a) 11print(a) # [3, 1] 12 13b = 1 14func_2(b) 15print(b) # 1

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

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

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

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

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

stdio

2019/02/11 00:34

もう少しネット等で調べてから質問して下さい。
MagMag

2019/02/11 01:42

失礼しました。ただ、この方式にする利点というのは、調べてもわかりませんでした、、、。
guest

回答5

0

ベストアンサー

標準モジュールのrandom.shuffleなんかその動作です。

可読性が悪いので、returnで引数を明示する方がベターかと思いますが、このコード(func_1)の利点はありますでしょうか?

複数の要素を書き換えたりといった、ある程度複雑な処理だとこれがベターな場合があります。

結果のリストを作って返す? メモリ二倍食いますね。オーバーヘッドもかさみます(pythonだとちまちま代入して書き換えるより、リスト丸ごと内包表記で作った方が早い可能性もあるとはいえ)。

func_1では値が上書きされるのに、func_2ではなぜ値が上書きされないのでしょうか?

func_2は関数のローカル名前空間に対する操作、func_1はリストオブジェクトに対する操作だから。

変数に代入するという行為は名前空間上で名前にオブジェクトを束縛するという行為です。名前空間を抜ければ何も影響は残りません。

x[0]に代入するのだと、x__setitem__メソッドの呼び出しとして内部的には処理されます。全くの別物です。

リストや辞書の要素に代入する場合、あとオブジェクトの属性に代入する場合は変数への代入とは違う処理だと思ってください。構文上で区別できないので混乱しますが。

投稿2019/02/11 02:17

hayataka2049

総合スコア30933

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

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

MagMag

2019/02/13 23:29

よくわかりました。ありがとうございます!
guest

0

図で表すとこんな感じです(矢印がポインター)。

イメージ説明

投稿2019/02/11 07:50

karamarimo

総合スコア2551

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

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

MagMag

2019/02/13 23:30

わかりやすい図を入れていただき、ありがとうございました!
guest

0

可読性が悪いので、returnで引数を明示する方がベターかと思いますが、このコード(func_1)の利点はありますでしょうか?

目的によりけりです。

func_1では値が上書きされるのに、func_2ではなぜ値が上書きされないのでしょうか?

別に、関数だからと言うわけじゃ無いです。

Python

1a = [1,1] 2x = a 3x[0] = 3 4print(a) # [3, 1] 5 6b = 1 7x = b 8x = 3 9print(b) # 1

オブジェクトを変数に代入すると、オブジェクト自体が変数の中に入ると言うよりは、オブジェクトへのリンクが変数に入るので、他の変数に代入してもリンクがコピーされるだけで、両方のリンクが指すオブジェクトは同じものです。

投稿2019/02/11 01:20

otn

総合スコア84423

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

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

MagMag

2019/02/13 12:58

ありがとうございます。参照とコピーというやつですね。習ってはいたのですが、本件と関連していると思い至りませんでした。よくわかりました!
guest

0

関数の引数は、呼び出し側からコピーされて渡されるもんなんで、関数側で変更しても呼び出し側では変わりません

ましかし、配列やクラスなどを引数にすると、配列自身がそのまま渡されるわけでなくて、そのオブジェクトアドレスがコピーされて関数に渡されます
#実際に配置されてる配列の位置が渡される

関数内では、そのアドレスをもとに、ナカミの位置にある変数を変えてしまうために、
もともとの配列(やクラス)のナカミが変わってしまう、ということですね。
#あかん、わかりにくい。。。

投稿2019/02/11 00:49

編集2019/02/11 01:06
y_waiwai

総合スコア87719

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

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

hayataka2049

2019/02/11 02:32 編集

pythonなのでintだろうと「参照型」で、アドレスのコピーで渡ります(内部の実装の話なのでこの言い方もどうかと思うけど)。いわゆる値渡しはpythonにはないです。
MagMag

2019/02/13 12:53

y_waiwaiさんの説明でなるほど、と思ったのですが、そうなんですね。「コピーで渡る」というところが、func_1とfunc_2の挙動の違い(に見える)かまだ理解できていないですが、他の方の回答も確認します。
guest

0

値を返すべきかという問題についてのみ回答します。

命令型言語では、コマンド・クエリー分離原則などと呼ばれる方針があります。関数やメソッドを作用に応じて2種類に分け、作用が混じり合った関数(あるいはメソッド)を作るべきではない、という考え方です。

この考えによると、コマンドは副作用を生じさせることが目的の関数であり、この場合その関数は値を返すべきではない。また、クエリーは、欲しい値を返すことが目的の関数であり、この場合その関数は副作用を生じさせてはいけない、ということになります。

問題のfunc_1は引数となる配列の状態を変更する関数なので、コマンドに分類されます。そういう訳で、コマンド・クエリー分離原則に従うと、func_1は値を返すべきでないということになるのです。

投稿2019/02/11 15:30

編集2019/02/11 15:50
kts_h

総合スコア207

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

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

MagMag

2019/02/13 23:37

ありがとうございます。「コマンド・クエリー分離原則」ぐぐってみたら難しそうですが、少し読み込んでみようと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問