変数を参照渡しするにはどうすればよいですか?


2628

Pythonのドキュメントでは、パラメーターが参照によって渡されるのか値によって渡されるのかが不明確であり、次のコードは変更されていない値 'Original'を生成します

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.change(self.variable)
        print(self.variable)

    def change(self, var):
        var = 'Changed'

実際の参照で変数を渡すために何かできることはありますか?


23
簡単な説明/説明については、このスタックオーバーフローの質問に対する最初の回答をご覧ください。文字列は不変であるため、変更されず、新しい変数が作成されます。したがって、「外部」変数の値は変わりません。
PhilS 2009年

10
BlairConradの回答のコードは適切ですが、DavidCournapeauとDarenThomasによって提供された説明は正しいです。
イーサンファーマン、2012年

70
選択した回答を読む前に、この短いテキストを読むことを検討してください。他の言語には「変数」があり、Pythonには「名前」があります。「変数」と「参照」の代わりに「名前」と「オブジェクト」について考えてください。そうすれば、同様の問題をたくさん回避する必要があります。
lqc

24
なぜこれが不必要にクラスを使用するのですか?これはJavaではありません:P
Peter R 14年

2
別の回避策は、次のようなラッパー「参照」を作成することです:ref = type( ''、()、{'n':1})stackoverflow.com/a/1123054/409638
robert

回答:


2831

引数は割り当てによって渡されます。この背後にある理論的根拠は2つあります。

  1. 渡されたパラメータは、実際には、基準オブジェクトへの(しかし、参照は値によって渡されます)
  2. 一部のデータ型は変更可能ですが、その他は変更できません

そう:

  • 変更可能なオブジェクトをメソッドに渡すと、メソッドはその同じオブジェクトへの参照を取得し、それを喜んで変更することができますが、メソッドで参照を再バインドすると、外側のスコープはそれについて何も知りません。完了しても、外部参照はまだ元のオブジェクトを指しています。

  • 不変オブジェクトをメソッドに渡しても、外部参照を再バインドすることはできず、オブジェクトを変更することもできません。

より明確にするために、いくつかの例を挙げましょう。

リスト-可変タイプ

メソッドに渡されたリストを変更してみましょう:

def try_to_change_list_contents(the_list):
    print('got', the_list)
    the_list.append('four')
    print('changed to', the_list)

outer_list = ['one', 'two', 'three']

print('before, outer_list =', outer_list)
try_to_change_list_contents(outer_list)
print('after, outer_list =', outer_list)

出力:

before, outer_list = ['one', 'two', 'three']
got ['one', 'two', 'three']
changed to ['one', 'two', 'three', 'four']
after, outer_list = ['one', 'two', 'three', 'four']

渡されたパラメータはの参照outer_listであり、そのコピーではないため、変更リストメソッドを使用してパラメータを変更し、変更を外部スコープに反映させることができます。

次に、パラメーターとして渡された参照を変更しようとするとどうなるかを見てみましょう。

def try_to_change_list_reference(the_list):
    print('got', the_list)
    the_list = ['and', 'we', 'can', 'not', 'lie']
    print('set to', the_list)

outer_list = ['we', 'like', 'proper', 'English']

print('before, outer_list =', outer_list)
try_to_change_list_reference(outer_list)
print('after, outer_list =', outer_list)

出力:

before, outer_list = ['we', 'like', 'proper', 'English']
got ['we', 'like', 'proper', 'English']
set to ['and', 'we', 'can', 'not', 'lie']
after, outer_list = ['we', 'like', 'proper', 'English']

the_listパラメータは値で渡されたため、新しいリストを割り当てても、メソッドの外部のコードが見ることができませんでした。これthe_listouter_list参照のコピーでありthe_list、新しいリストをouter_list指すようにしましたが、指す場所を変更する方法がありませんでした。

文字列-不変タイプ

不変なので、文字列の内容を変更するために私たちにできることは何もありません

では、参照を変更してみましょう

def try_to_change_string_reference(the_string):
    print('got', the_string)
    the_string = 'In a kingdom by the sea'
    print('set to', the_string)

outer_string = 'It was many and many a year ago'

print('before, outer_string =', outer_string)
try_to_change_string_reference(outer_string)
print('after, outer_string =', outer_string)

出力:

before, outer_string = It was many and many a year ago
got It was many and many a year ago
set to In a kingdom by the sea
after, outer_string = It was many and many a year ago

繰り返しになりthe_stringますが、パラメーターは値で渡されるため、新しい文字列をパラメーターに割り当てても、メソッドの外部のコードが見ることができませんでした。これthe_stringouter_string参照のコピーでありthe_string、新しい文字列をouter_string指すようにしましたが、指す場所を変更する方法がありませんでした。

これで状況が少しよくなるといいですね。

編集:これは@Davidが最初に尋ねた「実際の参照によって変数を渡すために何かできることはありますか?」という質問に答えないことが指摘されています。それに取り組みましょう。

どうすればこれを回避できますか?

@Andreaの答えが示すように、新しい値を返すことができます。これにより、渡される方法は変わりませんが、必要な情報を取り戻すことができます。

def return_a_whole_new_string(the_string):
    new_string = something_to_do_with_the_old_string(the_string)
    return new_string

# then you could call it like
my_string = return_a_whole_new_string(my_string)

戻り値の使用を避けたい場合は、値を保持するクラスを作成して関数に渡すか、リストのような既存のクラスを使用できます。

def use_a_wrapper_to_simulate_pass_by_reference(stuff_to_change):
    new_string = something_to_do_with_the_old_string(stuff_to_change[0])
    stuff_to_change[0] = new_string

# then you could call it like
wrapper = [my_string]
use_a_wrapper_to_simulate_pass_by_reference(wrapper)

do_something_with(wrapper[0])

これは少し面倒ですが。


165
次に、Cでも同じです。「参照」を渡すと実際には参照でを渡します...「参照」を定義します:P
Andrea Ambu

104
私はあなたの言葉を理解しているのかわかりません。私はしばらくCのゲームから離れていましたが、そのときは「参照渡し」はありませんでした。あなたは物事を渡すことができ、それは常に値渡しなので、パラメーターリストにあったものは何でもコピーされました。しかし、時々、それはメモリの一部(プリミティブ、配列、構造体など)をたどることができるポインタでしたが、外部スコープからコピーされたポインタを変更できませんでした-関数を使い終わったとき、元のポインタはまだ同じアドレスを指しています。C ++は、動作が異なる参照を導入しました。
ブレアコンラッド

34
@Zacボウリング実際の意味で、あなたの言っていることがこの回答にどのように関連しているかはわかりません。Pythonの新人は、REF / valで渡しについて知りたいと思った場合は、この答えからお持ち帰りは次のとおりです。1 -あなたができる限り、変数の「外」の値を変更するために、関数は引数として受け取ることの参照を使用します新しいオブジェクトを参照するためにパラメータを再割り当てしないため。2-不変タイプに割り当てると、常に新しいオブジェクト作成され、外部変数への参照が解除されます。
カムジャクソン

11
@CamJackson、もっと良い例が必要です-数値もPythonでは不変オブジェクトです。それに、と言うことは真実ではありません任意の等号の左側に添字なしの割り当てはそれが不変であるかどうか、新しい名前をオブジェクトに再割り当てされますか?def Foo(alist): alist = [1,2,3]ではない発信者の観点から、リストの内容を変更します。
Mark Ransom

59
-1。示されているコードは良好であり、その方法は完全に間違っています。理由に関する正しい説明については、DavidCournapeauまたはDarenThomasの回答を参照してください。
イーサンファーマン

685

問題は、Pythonの変数の誤解から生じます。ほとんどの従来の言語に慣れている場合は、次のシーケンスで何が起こるかについてのメンタルモデルがあります。

a = 1
a = 2

あなたはそれaが値を格納するメモリの場所であると信じ1、そして値を格納するために更新されます2。これはPythonでの動作方法ではありません。代わりに、a値を持つオブジェクトへの参照として開始し、値1を持つオブジェクトへの参照として再割り当てされます2。これらの2つのオブジェクトはa、最初のオブジェクトを参照しなくなっても、引き続き共存する可能性があります。実際、それらはプログラム内の他の参照でいくつでも共有できます。

パラメータを指定して関数を呼び出すと、渡されたオブジェクトを参照する新しい参照が作成されます。これは、関数呼び出しで使用された参照とは異なるため、その参照を更新してそれを参照する方法はありません新しいオブジェクト。あなたの例では:

def __init__(self):
    self.variable = 'Original'
    self.Change(self.variable)

def Change(self, var):
    var = 'Changed'

self.variable文字列オブジェクトへの参照'Original'です。呼び出すと、オブジェクトへのChange2番目の参照が作成さvarれます。関数内で、参照varを別の文字列オブジェクト'Changed'に再割り当てしますが、参照self.variableは独立しており、変更されません。

これを回避する唯一の方法は、可変オブジェクトを渡すことです。どちらの参照も同じオブジェクトを参照しているため、オブジェクトへの変更は両方の場所に反映されます。

def __init__(self):         
    self.variable = ['Original']
    self.Change(self.variable)

def Change(self, var):
    var[0] = 'Changed'

106
良い簡潔な説明。「関数を呼び出すとき...」という段落は、「Python関数のパラメーターは参照であり、値によって渡される」というかなり不可解なフレーズについて聞いた中で最も良い説明の1つです。その段落だけを理解すれば、他のすべてのものが理にかなっており、そこからの論理的な結論として流れます。次に、新しいオブジェクトを作成するときと、既存のオブジェクトを変更するときを認識する必要があります。
カムジャクソン

3
しかし、どうすれば参照を再割り当てできますか?「var」のアドレスを変更できないと思ったが、「Changed」という文字列が「var」のメモリアドレスに格納されるようになった。説明では、「変更済み」と「元の」がメモリ内の別の場所に属しているように見え、 'var'を別のアドレスに切り替えるだけです。あれは正しいですか?
Glassjawed、2007年

9
@Glassjawed、私はあなたがそれを手に入れていると思います。「Changed」と「Original」は、異なるメモリアドレスにある2つの異なる文字列オブジェクトであり、「var」は一方を指すことから他方を指すように変化します。
Mark Ransom

id()関数を使用すると、Pythonが新しいオブジェクトを作成するときにそれが明らかになるため(とにかく)、問題を明確にするのに役立ちます。
Tim Richardson

1
最も簡単に言えば、@ MinhTranは、参照はオブジェクトを「参照」するものです。その物理的な表現はおそらくポインタですが、それは単なる実装の詳細です。それは本当に抽象的な概念です。
Mark Ransom、

329

他の答えはかなり長くて複雑なので、この単純な図を作成して、Pythonが変数とパラメーターを処理する方法を説明しました。 ここに画像の説明を入力してください


2
素敵で、カジュアルな見物人には明らかではない、中間的な割り当てがあるという微妙な違いを簡単に見つけることができます。+1
user22866

9
Aが変更可能かどうかは関係ありません。Bに別のものを割り当てても、Aは変更されません。オブジェクトが変更可能な場合は、変更できます。ただし、名前への直接の割り当てとは関係ありません
。– Martijn Pieters

1
@Martijnそうですね。可変性について言及している部分を削除しました。今はもっと簡単になるとは思いません。
Zenadix 2016年

4
更新ありがとうございます。ほとんどの人を混乱させるのはサブスクリプションへの割り当てです。例えばB[0] = 2、直接割り当て対、B = 2
Martijn Pieters

11
「AはBに割り当てられます。」あいまいではありませんか?普通の英語では、A=Bまたはのどちらかを意味すると思いますB=A
ハトシェプスト

240

これは値渡しでも参照渡しでもありません-オブジェクトによる呼び出しです。Fredrik Lundhによるこれを参照してください。

http://effbot.org/zone/call-by-object.htm

ここに重要な引用があります:

「...変数[名前]はオブジェクトではありません。他の変数で表すことも、オブジェクトから参照することもできません。」

あなたの例では、Changeメソッドが呼び出されると、名前空間が作成されます。そしてvar、その名前空間内の文字列オブジェクトの名前になります'Original'。そのオブジェクトは、2つの名前空間に名前を持ちます。次に、新しい文字列オブジェクトにvar = 'Changed'バインドvarするため、メソッドの名前空間はaboutを忘れ'Original'ます。最後に、その名前空間とその文字列は忘れられます'Changed'


21
私は買うのが難しいと思います。私にとってはJavaと同じですが、パラメーターはメモリー内のオブジェクトへのポインターであり、それらのポインターはスタックまたはレジスターを介して渡されます。
Luciano

10
これはjavaのようなものではありません。同じでない場合の1つは、不変オブジェクトです。自明な関数ラムダx:xについて考えます。これをx = [1、2、3]およびx =(1、2、3)に適用します。前者の場合、戻り値は入力のコピーで、後者の場合も同じです。
David Cournapeau

26
いいえ、それはオブジェクトに対するJavaのセマンティクスとまったく同じです。「最初のケースでは、戻り値は入力のコピーであり、2番目のケースでも同じです」という意味がわかりません。しかし、その声明は明らかに間違っているようです。
マイクグラハム

23
Javaとまったく同じです。オブジェクト参照は値で渡されます。別々に考えて誰でものためのPythonコードを添付しなければならないswap。このように、2つの参照を入れ替えることができます機能: a = [42] ; b = 'Hello'; swap(a, b) # Now a is 'Hello', b is [42]
cayhorstmann

24
Javaでオブジェクトを渡す場合は、Javaとまったく同じです。ただし、Javaにもプリミティブがあり、プリミティブの値をコピーすることによって渡されます。したがって、その場合は異なります。
Claudiu 2013

174

参照渡しや値渡しでなく、代入渡しされるものを考えてください。そうすれば、通常の割り当て中に何が起こるかを理解している限り、何が起こっているかは常に明確です。

したがって、リストを関数/メソッドに渡すとき、リストはパラメーター名に割り当てられます。リストに追加すると、リストが変更されます。関数内でリスト再割り当てしても、元のリストは変更されません。

a = [1, 2, 3]
b = a
b.append(4)
b = ['a', 'b']
print a, b      # prints [1, 2, 3, 4] ['a', 'b']

不変の型は変更できないため、値によって渡されるように見えます。intを関数に渡すと、intを関数のパラメーターに割り当てることになります。再割り当てのみが可能ですが、元の変数の値は変更されません。


3
一見すると、この回答は元の質問を回避しているようです。もう一度読んだ後、これで問題が非常に明確になることに気づきました。この「名前の割り当て」の概念に対する適切なフォローアップは、ここにあります。Pythonのようなコード:慣用的なPython
Christian Groleau

65

Effbot(別名Fredrik Lundh)は、Pythonの変数受け渡しスタイルをcall-by-objectとして説明しています:http : //effbot.org/zone/call-by-object.htm

オブジェクトはヒープに割り当てられ、それらへのポインタはどこにでも渡すことができます。

  • などの割り当てを行うx = 1000と、現在のネームスペースの文字列「x」を、1,000を含む整数オブジェクトへのポインタにマップする辞書エントリが作成されます。

  • で「x」を更新するとx = 2000、新しい整数オブジェクトが作成され、新しいオブジェクトを指すようにディクショナリが更新されます。古い千のオブジェクトは変更されていません(オブジェクトが他に参照されているかどうかによって、生存しているかどうかは不明です)。

  • などの新しい割り当てを行うと、y = x「x」のエントリと同じオブジェクトを指す新しい辞書エントリ「y」が作成されます。

  • 文字列や整数のようなオブジェクトは不変です。これは単に、オブジェクトの作成後にオブジェクトを変更できるメソッドがないことを意味します。たとえば、整数オブジェクト1000が作成されると、変更されることはありません。数学は、新しい整数オブジェクトを作成することによって行われます。

  • リストのようなオブジェクトは変更可能です。これは、オブジェクトを指すものであれば何でもオブジェクトの内容を変更できることを意味します。たとえば、x = []; y = x; x.append(10); print yが印刷されます[10]。空のリストが作成されました。「x」と「y」はどちらも同じリストを指します。追記方法が突然変異(更新)する(データベースにレコードを追加するような)リストオブジェクトと結果は、(データベースの更新は、そのデータベースへのすべての接続に見えるであろうと同じように)「X」と「Y」の両方に表示されます。

問題が明確になることを願っています。


2
開発者からこのことを学んで本当に感謝しています。id()peprの答えが示唆するように、関数がポインタの(オブジェクト参照の)値を返すことは本当ですか?
Honest Abe

4
@HonestAbeはい、CPythonではid()がアドレスを返します。しかし、PyPyやJythonなどの他のpythonでは、id()は単なる一意のオブジェクト識別子です。
レイモンドヘッティンガー2014年

59

技術的には、Pythonは常に参照渡しの値を使用します。私の発言を裏付けるために、他の答えを繰り返します。

Pythonは常に参照渡しの値を使用します。例外はありません。変数を代入すると、参照値がコピーされます。例外なし。変数は、参照値にバインドされた名前です。常に。

参照値は、ターゲットオブジェクトのアドレスと考えることができます。アドレスは、使用時に自動的に逆参照されます。このように、参照値を操作すると、ターゲットオブジェクトを直接操作しているように見えます。しかし、その間には常に参照があり、ターゲットにジャンプするためにもう1ステップあります。

Pythonが参照渡しを使用していることを証明する例を次に示します。

引数を渡す例証された例

引数が値で渡された場合、アウターlstは変更できませんでした。緑はターゲットオブジェクト(黒は内部に格納された値、赤はオブジェクトタイプです)、黄色は参照値が内部にあるメモリです-矢印として描画されます。青い実線の矢印は、(青い破線の矢印パスを介して)関数に渡された参照値です。醜い濃い黄色は内部辞書です。(実際には、緑色の楕円としても描画できます。色と形は、それが内部であることを示しているだけです。)

id()組み込み関数を使用して、参照値が何であるか(つまり、ターゲットオブジェクトのアドレス)を知ることができます。

コンパイルされた言語では、変数は型の値を取得できるメモリ空間です。Pythonでは、変数は、ターゲットオブジェクトへの参照値を保持する参照変数にバインドされた名前(内部的に文字列としてキャプチャされます)です。変数の名前は内部ディクショナリのキーです。そのディクショナリアイテムの値の部分には、ターゲットへの参照値が格納されます。

参照値はPythonでは非表示になっています。参照値を格納するための明示的なユーザータイプはありません。ただし、すべてのコンテナは要素をターゲットオブジェクトへの参照としても格納するため、リスト変数(または他の適切なコンテナタイプの要素)を参照変数として使用できます。つまり、要素は実際にはコンテナ内に含まれていません。要素への参照のみが含まれています。


1
実際には、これは参照値によってパスが確認されています。例は良くありませんでしたが、この回答の+1。
BugShotGG 2012年

30
新しい用語の考案(「参照値による受け渡し」や「オブジェクトによる呼び出し」など)は役に立ちません。「(値|参照|名前)による呼び出し」は標準的な用語です。「参照」は標準的な用語です。値による参照の受け渡しは、標準的な用語を使用して、Python、Java、およびその他の言語のホストの動作を正確に表します。
cayhorstmann 2012

4
@cayhorstmann:問題は、Python変数が他の言語と同じ用語の意味を持たないことです。このように、参照による呼び出しはここではうまく適合しません。また、参照という用語をどのように正確に定義しますか?非公式には、Pythonの方法はオブジェクトのアドレスを渡すものとして簡単に説明できます。しかし、Pythonの潜在的に分散された実装には適合しません。
PEPrと

1
私はこの答えが好きですが、例が本当に流れを助けているか、傷つけているかを検討するかもしれません。また、「参照値」を「オブジェクト参照」に置き換えた場合は、「公式」と見なすことができる用語を使用していることになります。次のように、関数の定義
Honest Abe

2
読み込みその引用、の最後に示されている脚注があります:「実は、オブジェクトの参照による呼び出しは、可変オブジェクトが渡された場合、呼び出し側は任意のは、それに呼び出し先を変更します表示されますので、より良い説明だろう... 「私は、混乱が他の言語で確立された用語を適合させようとすることによって引き起こされるということに同意します。意味論はさておき、理解する必要があるのは、辞書/名前空間、名前バインディング操作、および名前→ポインタ→オブジェクトの関係です(ご存知のとおり)。
Honest Abe

56

Pythonには変数はありません

パラメータの受け渡しを理解するための鍵は、「変数」について考えるのをやめることです。Pythonには名前とオブジェクトがあり、それらは一緒に変数のように見えますが、常に3つを区別すると便利です。

  1. Pythonには名前とオブジェクトがあります。
  2. 割り当ては、名前をオブジェクトにバインドします。
  3. 引数を関数に渡すと、名前(関数のパラメーター名)もオブジェクトにバインドされます。

これですべてです。可変性はこの質問には関係ありません。

例:

a = 1

これによりa、値1を保持する整数型のオブジェクトに名前がバインドされます。

b = x

これbにより、名前xが現在バインドされているオブジェクトと同じオブジェクトにバインドされます。その後、名前bxもう名前とは関係ありません。

Python 3言語リファレンスのセクション3.14.2を参照してください。

質問の例の見方

質問に示すコードでは、文は、self.Change(self.variable)名前を結合するvar(関数の範囲でChange値を保持するオブジェクトへの)'Original'と割り当てvar = 'Changed'(関数の体内のChange他のオブジェクトへの(それが起こる。)再び同じ名前がその割り当てを文字列も保持しますが、完全に別のものにすることもできます)

参照渡し方法

したがって、変更したいものが変更可能なオブジェクトである場合、すべてが事実上参照によって渡されるため、問題はありません。

それがある場合は不変オブジェクト(例えばAブール値、数値、文字列)、移動するための方法は、変更可能なオブジェクトでラップすることです。
このための迅速で汚い解決策は、1つの要素のリストです(代わりにself.variable[self.variable]関数pass と関数modify var[0])。
よりパイソン的なアプローチは、ささいな1属性クラスを導入することです。関数はクラスのインスタンスを受け取り、属性を操作します。


20
「Pythonには変数がない」というのはばかげて紛らわしいスローガンであり、私は人々にそれを言わせないように本当に願っています... :(この答えの残りは良いです!
Ned Batchelder

9
衝撃的かもしれませんが、ばかげていません。また、混乱することもないと思います。これにより、受信者の説明が開かれ、「変数の代わりに何があるのか​​」という有用な態度が得られると期待しています。(はい、マイレージは異なる場合があります。)
Lutz Prechelt

13
また、Javascriptには変数がないと言えるでしょうか。Pythonと同じように機能します。また、Java、Ruby、PHPなども、「Pythonの変数はCの変数とは異なる働きをする」というより優れた教育手法だと思います。
Ned Batchelder 2014年

9
はい、Javaには変数があります。Python、JavaScript、Ruby、PHPなどもそうです。Javaでint変数を宣言することはありませんが、宣言Integerしません。どちらも変数を宣言します。Integer変数は、対象となるint変数はプリミティブです。例として、を表示することにより、変数がどのように機能するかを示しましたa = 1; b = a; a++ # doesn't modify b。これはPythonでも同じです(Python += 1にはないので、これを使用します++)。
Ned Batchelder 2014年

1
「変数」の概念は複雑で、多くの場合あいまいです。変数は、名前で識別される値のコンテナーです。Pythonでは、値はオブジェクト、コンテナーはオブジェクト(問題を参照)であり、名前は実際には別のものです。この方法で変数を正確に理解することははるか難しいと思います。名前とオブジェクトの説明はもっと難しいように見えますが、実際はもっと単純です。
Lutz Prechelt、2015年

43

私が通常使用する簡単なトリックは、それをリストにラップすることです:

def Change(self, var):
    var[0] = 'Changed'

variable = ['Original']
self.Change(variable)      
print variable[0]

(そうですね、これは不便かもしれませんが、これを行うのに十分簡単な場合もあります。)


7
+1は、Pythonが参照渡しを行わないという問題の本質的な回避策を提供する少量のテキストに使用します。(こことこのページの任意の場所に当てはまる後続のコメント/質問として:pythonがC#のように「ref」キーワードを提供できない理由は明らかではありません。これは、呼び出し元の引数を次のようなリストにラップするだけですこれと、関数内の引数への参照をリストの0番目の要素として扱います。)
M Katz

5
いいね。参照渡しするには、[]で囲みます。
Justas

38

(編集-ブレアは彼の非常に人気のある回答を正確に更新しました)

現在の投稿数が最も多い(ブレアコンラッドによる)現在の投稿は、その結果に関しては正しいが、誤解を招くものであり、その定義に基づいて境界が正しくないことに注意することが重要だと思います。ユーザーが参照渡しまたは値渡しのいずれかを許可する多くの言語(Cなど)がありますが、Pythonはそれらの1つではありません。

David Cournapeauの回答は実際の回答を示し、定義が正しくないのにブレアコンラッドの投稿の動作が正しいように見える理由を説明しています。

一部のデータ(「値」または「参照」)を送信する必要があるため、Pythonが値渡しである限り、すべての言語は値渡しです。ただし、これは、CプログラマーがPythonが考えるような意味で、Pythonが値渡しであることを意味するものではありません。

この動作が必要な場合は、ブレアコンラッドの答えで結構です。しかし、Pythonが値渡しでも参照渡しでもない理由の要点を知りたい場合は、David Cournapeauの回答を読んでください。


4
すべての言語が価値によって呼び出されるということは単に真実ではありません。C ++またはPascal(そして私が知らない他の多くの確かに)では、参照によって呼び出します。たとえば、C ++では、void swap(int& x, int& y) { int temp = x; x = y; y = temp; }渡された変数を入れ替えます。Pascalでは、のvar代わりに使用します&
cayhorstmann 2012

2
私はずっと前にこれに返信したと思っていましたが、私にはわかりません。完全を期すために-cayhorstmannは私の答えを誤解しました。ほとんどの人がC / C ++について最初に学ぶ言葉ですべてが価値あるものであるとは言っていませんでした。それは単にことだったいくつかの値(値、名前、ポインタなど)を渡され、ブレアのオリジナルの答えで使用される用語が不正確だったこと。
神戸ジョン2015年

24

ここで本当に良い答えを得ました。

x = [ 2, 4, 4, 5, 5 ]
print x  # 2, 4, 4, 5, 5

def go( li ) :
  li = [ 5, 6, 7, 8 ]  # re-assigning what li POINTS TO, does not
  # change the value of the ORIGINAL variable x

go( x ) 
print x  # 2, 4, 4, 5, 5  [ STILL! ]


raw_input( 'press any key to continue' )

2
ええ、ただし、x = [2、4、4、5、5]、y = x、X [0] = 1を実行する場合、x#[1、4、4、5、5]を印刷するy#[1を印刷する、
4、4、5、5

19

この場合、varメソッドでタイトルが付けられた変数にChangeへの参照が割り当てられ、self.variableすぐに文字列をに割り当てますvar。を指さなくなりましたself.variable。次のコードスニペットは、varおよびが指すデータ構造self.variable、この場合はリストを変更した場合にどうなるかを示しています。

>>> class PassByReference:
...     def __init__(self):
...         self.variable = ['Original']
...         self.change(self.variable)
...         print self.variable
...         
...     def change(self, var):
...         var.append('Changed')
... 
>>> q = PassByReference()
['Original', 'Changed']
>>> 

他の誰かがこれをさらに明確にすることができると私は確信しています。


18

Pythonの代入渡しスキームは、C ++の参照パラメーターオプションとまったく同じではありませんが、実際には、C言語(およびその他)の引数渡しモデルと非常によく似ています。

  • 不変の引数は、「値によって」効果的に渡されます。整数や文字列などのオブジェクトは、コピーではなくオブジェクト参照で渡されますが、不変のオブジェクトを適切に変更できないため、効果はコピーを作成するのとよく似ています。
  • 可変引数は「ポインタによって効果的に渡されます。リストや辞書などのオブジェクトもオブジェクト参照によって渡されます。これは、Cが配列をポインターとして渡す方法に似ています。可変オブジェクトは、C配列と同様に、関数内で変更できます。

16

変更可能なオブジェクトが必要であると述べることができますが、グローバル変数を確認することをお勧めします。グローバル変数が役立つか、この種の問題を解決できるためです!

http://docs.python.org/3/faq/programming.html#what-are-the-rules-for-local-and-global-variables-in-python

例:

>>> def x(y):
...     global z
...     z = y
...

>>> x
<function x at 0x00000000020E1730>
>>> y
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'y' is not defined
>>> z
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'z' is not defined

>>> x(2)
>>> x
<function x at 0x00000000020E1730>
>>> y
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'y' is not defined
>>> z
2

5
私は同様の応答を投稿するように誘惑されました。元の質問者は、彼が望んでいたのは実際に関数間で共有されるグローバル変数を使用することだったことを知らなかったかもしれません。これが私が共有したリンクです:stackoverflow.com/questions/423379/…@Tim への回答として、Stack Overflowは質問と回答のサイトであるだけでなく、より強力でより微妙なものになる参照知識の膨大なリポジトリですアクティブなwikiのように入力を増やします。
Max P Magee

13

ここで答えの洞察はたくさんありますが、追加のポイントはここでは明示的に明示されていないと思います。Pythonドキュメントからの引用https://docs.python.org/2/faq/programming.html#what-are-the-rules-for-local-and-global-variables-in-python

「Pythonでは、関数内でのみ参照される変数は暗黙的にグローバルです。変数に関数の本体内のどこかに新しい値が割り当てられている場合、その変数はローカルであると見なされます。変数に関数内で新しい値が割り当てられている場合、変数は暗黙的にローカルであり、 'グローバル'として明示的に宣言する必要があります。最初は少し意外ですが、ちょっと考えてみるとこれが説明されます。一方で、割り当てられた変数にグローバルを要求すると、意図しない副作用が発生しにくくなります。一方、すべてのグローバル参照にグローバルが必要な場合は、常にグローバルを使用します。組み込み関数またはインポートされたモジュールのコンポーネントへのすべての参照をグローバルとして宣言する必要があります。副作用を特定するためのグローバル宣言の有用性を無効にするでしょう。」

可変オブジェクトを関数に渡す場合でも、これは適用されます。また、オブジェクトへの割り当てと関数内でのオブジェクトの操作の動作が異なる理由を明確に説明します。

def test(l):
    print "Received", l , id(l)
    l = [0, 0, 0]
    print "Changed to", l, id(l)  # New local object created, breaking link to global l

l= [1,2,3]
print "Original", l, id(l)
test(l)
print "After", l, id(l)

与える:

Original [1, 2, 3] 4454645632
Received [1, 2, 3] 4454645632
Changed to [0, 0, 0] 4474591928
After [1, 2, 3] 4454645632

したがって、グローバルとして宣言されていないグローバル変数への割り当ては、新しいローカルオブジェクトを作成し、元のオブジェクトへのリンクを解除します。


10

pass by objectPythonで使用されている概念の簡単な(私はそう思います)説明です。
関数にオブジェクトを渡すと、オブジェクト自体への参照ではなく、オブジェクト自体が渡されます(Pythonのオブジェクトは、実際には他のプログラミング言語で値と呼ぶものです)。言い換えれば、あなたが呼び出すとき:

def change_me(list):
   list = [1, 2, 3]

my_list = [0, 1]
change_me(my_list)

実際のオブジェクト-[0、1](他のプログラミング言語では値と呼ばれる)が渡されています。したがって、実際には関数change_meは次のようなことを試みます。

[0, 1] = [1, 2, 3]

これは明らかに関数に渡されるオブジェクトを変更しません。関数が次のようになっている場合:

def change_me(list):
   list.append(2)

次に、呼び出しは次のようになります。

[0, 1].append(2)

これは明らかにオブジェクトを変更します。この答えはそれをよく説明しています。


2
問題は、割り当てが予想とは異なる何かを行うことです。これlist = [1, 2, 3]により、別のlist名前を再利用し、最初に渡されたオブジェクトを忘れてしまいます。ただし、試すことはできますlist[:] = [1, 2, 3](ちなみにlist、変数の名前は間違っています。考えること[0, 1] = [1, 2, 3]はまったくナンセンスです。とにかく、オブジェクト自体が渡されるとはどういう意味ですか?関数にコピーされるものは何ですか?
pepr

@peprオブジェクトはリテラルではありません。それらはオブジェクトです。それらについて話す唯一の方法はそれらにいくつかの名前を与えることです。そのため、一度理解すれば非常に簡単ですが、説明が非常に複雑になります。:-)
Veky

@Veky:私はそれを知っています。とにかく、リストリテラルはリストオブジェクトに変換されます。実際、Pythonのオブジェクトは名前なしで存在でき、名前を付けなくても使用できます。そして、それらを匿名オブジェクトについて考えることができます。オブジェクトがリストの要素であると考えてください。名前は必要ありません。リストにインデックスを付けるか、リストを反復処理することで、それらにアクセスできます。とにかく、私は主張すること[0, 1] = [1, 2, 3]は単に悪い例です。Pythonにはそのようなものはありません。
14年

@pepr:私は必ずしもPython定義の名前を意味するのではなく、普通の名前を意味します。もちろんalist[2]、リストの3番目の要素の名前としてカウントされます。しかし、私はあなたの問題が何であったかを誤解したと思います。:-)
Veky

ああ。私の英語は明らかに私のPythonよりもはるかに悪いです。:-)もう一度試してみます。私はあなたがそれらについて話すためにオブジェクトにいくつかの名前を付ける必要があると言いました。その「名前」によって、「Pythonで定義された名前」を意味していませんでした。私はPythonのメカニズムを知っています。心配しないでください。
Veky、2014年

8

このものがPythonでどのように機能するかについてのすべてのすばらしい説明は別として、問題に対する単純な提案はありません。オブジェクトとインスタンスを作成しているように見えるので、インスタンス変数を処理して変更するpythonicの方法は次のとおりです。

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.Change()
        print self.variable

    def Change(self):
        self.variable = 'Changed'

インスタンスメソッドでは、通常、selfインスタンス属性へのアクセスを参照します。通常__init__、インスタンスメソッドでインスタンス属性を設定し、それらを読み取りまたは変更します。そのためself、最初の引数としてals をに渡しますdef Change

別の解決策は、次のような静的メソッドを作成することです。

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.variable = PassByReference.Change(self.variable)
        print self.variable

    @staticmethod
    def Change(var):
        var = 'Changed'
        return var

6

言語がそれを可能にしていない場合でも、参照によってオブジェクトを渡すための小さなトリックがあります。これはJavaでも機能し、1つの項目を持つリストです。;-)

class PassByReference:
    def __init__(self, name):
        self.name = name

def changeRef(ref):
    ref[0] = PassByReference('Michael')

obj = PassByReference('Peter')
print obj.name

p = [obj] # A pointer to obj! ;-)
changeRef(p)

print p[0].name # p->name

醜いハックですが、機能します。;-P


pオブジェクトを格納する変更可能なリストオブジェクトへの参照objです。参照「p」はに渡されchangeRefます。内部changeRefではref、同じリストオブジェクトをp指す新しい参照が作成されます(新しい参照はと呼ばれます)。ただし、リストは変更可能であるため、リストへの変更は両方の参照で表示されます。この場合、ref参照を使用してインデックス0のオブジェクトを変更し、その後PassByReference('Michael')オブジェクトを格納します。リストオブジェクトのref変更はを使用して行われましたが、この変更はに表示されpます。
Minh Tran

したがって、参照pref単一のオブジェクトを格納するリストオブジェクトをポイントしますPassByReference('Michael')。したがって、それはをp[0].name返しますMichael。もちろん、ref現在は対象外となっており、ガベージコレクションの対象になる可能性がありますが、すべて同じです。
Minh Tran

ただし、参照に関連付けられている元のオブジェクトのプライベートインスタンス変数変更していません。実際には、が返されます。前述のコメントは、定義が与えられていることを前提としています。namePassByReferenceobjobj.namePeterMark Ransom
Minh Tran

ちなみに、それがハックであることに同意しません(これは、機能するものを指しますが、理由が不明、テストされていない、または実装者によって意図されていない)。リスト内の1つのPassByReferenceオブジェクトを別のPassByReferenceオブジェクトに置き換え、2つのオブジェクトの後者を参照しました。
Minh Tran

5

次の方法を使用して、いくつかのFortranコードをPythonにすばやく変換しました。確かに、元の質問が提起されたため、参照渡しではありませんが、場合によっては簡単な回避策です。

a=0
b=0
c=0
def myfunc(a,b,c):
    a=1
    b=2
    c=3
    return a,b,c

a,b,c = myfunc(a,b,c)
print a,b,c

はい、これは私のユースケースでも「参照渡し」を解決します。基本的にaの値をクリーンアップしてdictを返す関数がありますdict。ただし、クリーンアップ中に明らかになる場合がありますが、システムの一部の再構築が必要です。したがって、関数は、クリーンな状態を返すだけでなくdict、再構築を通知できる必要があります。bool参照渡しを試みましたが、機能しません。これを解決する方法を考え出したところ、私はあなたの解決策(基本的にはタプルを返す)が最もうまく機能し、同時にハック/回避策ではない(IMHO)ことを発見しました。
カシミール

3

参照渡しはpythonにうまく適合するものではなく、めったに使用されるべきではありませんが、ローカル変数に現在割り当てられているオブジェクトを取得したり、呼び出された関数の内部からローカル変数を再度割り当てたりするために実際に機能するいくつかの回避策があります。

基本的な考え方は、そのアクセスを実行し、オブジェクトとして他の関数に渡したり、クラスに格納したりできる関数を用意することです。

1つの方法は、ラッパー関数で(globalグローバル変数の場合)またはnonlocal(関数のローカル変数の場合)を使用することです。

def change(wrapper):
    wrapper(7)

x = 5
def setter(val):
    global x
    x = val
print(x)

同じ考えが読書や del、変数の編集にも有効です。

読むだけの場合は、さらに短い方法があります lambda: xは、呼び出し時にxの現在の値を返すcallableを返すあります。これは、昔の言語で使用されていた「名前で呼び出す」のようなものです。

変数にアクセスするために3つのラッパーを渡すのは少し扱いに​​くいので、プロキシ属性を持つクラスにラップすることができます。

class ByRef:
    def __init__(self, r, w, d):
        self._read = r
        self._write = w
        self._delete = d
    def set(self, val):
        self._write(val)
    def get(self):
        return self._read()
    def remove(self):
        self._delete()
    wrapped = property(get, set, remove)

# left as an exercise for the reader: define set, get, remove as local functions using global / nonlocal
r = ByRef(get, set, remove)
r.wrapped = 15

Pythonの「リフレクション」サポートにより、スコープ内で関数を明示的に定義しなくても、特定のスコープ内で名前/変数を再割り当てできるオブジェクトを取得できます。

class ByRef:
    def __init__(self, locs, name):
        self._locs = locs
        self._name = name
    def set(self, val):
        self._locs[self._name] = val
    def get(self):
        return self._locs[self._name]
    def remove(self):
        del self._locs[self._name]
    wrapped = property(get, set, remove)

def change(x):
    x.wrapped = 7

def test_me():
    x = 6
    print(x)
    change(ByRef(locals(), "x"))
    print(x)

ここでは、ByRefクラスが辞書アクセスをラップしています。したがって、属性access to wrappedは、渡された辞書のアイテムアクセスに変換されます。組み込みの結果とlocalsローカル変数の名前を渡すことで、ローカル変数にアクセスすることになります。3.5の時点でのpythonのドキュメントでは、辞書の変更は機能しない可能性があるが、私にとっては機能するようです。


3

Pythonが値とそれらへの参照を処理する方法を考えると、任意のインスタンス属性を参照できる唯一の方法は名前によるものです。

class PassByReferenceIsh:
    def __init__(self):
        self.variable = 'Original'
        self.change('variable')
        print self.variable

    def change(self, var):
        self.__dict__[var] = 'Changed'

実際のコードでは、もちろん、dictルックアップにエラーチェックを追加します。


3

辞書は参照によって渡されるため、dict変数を使用して、参照された値をその中に格納できます。

# returns the result of adding numbers `a` and `b`
def AddNumbers(a, b, ref): # using a dict for reference
    result = a + b
    ref['multi'] = a * b # reference the multi. ref['multi'] is number
    ref['msg'] = "The result: " + str(result) + " was nice!" # reference any string (errors, e.t.c). ref['msg'] is string
    return result # return the sum

number1 = 5
number2 = 10
ref = {} # init a dict like that so it can save all the referenced values. this is because all dictionaries are passed by reference, while strings and numbers do not.

sum = AddNumbers(number1, number2, ref)
print("sum: ", sum)             # the return value
print("multi: ", ref['multi'])  # a referenced value
print("msg: ", ref['msg'])      # a referenced value

3

Pythonの参照渡しは、C ++ / Javaの参照渡しの概念とはかなり異なります。

  • Java&C#:プリミティブ型(文字列を含む)値渡し(コピー)、参照型は参照渡し(アドレスコピー)で、呼び出された関数のパラメーターで行われたすべての変更が呼び出し元に表示されます。
  • C ++:参照渡しまたは値渡しの両方が許可されます。パラメータが参照によって渡される場合、パラメータがconstとして渡されたかどうかに応じて、パラメータを変更することもしないこともできます。ただし、constかどうかに関係なく、パラメーターはオブジェクトへの参照を維持し、参照を呼び出し先の関数内の別のオブジェクトを指すように割り当てることはできません。
  • Python: Pythonは「オブジェクト参照による受け渡し」であり、「オブジェクト参照は値によって受け渡される」とよく言われます。[ここを読む] 1。呼び出し元と関数の両方が同じオブジェクトを参照していますが、関数のパラメーターは、呼び出し元のオブジェクトのコピーを保持している新しい変数です。C ++と同様に、パラメーターは変更することも、関数内で変更しないこともできます。これは、渡されるオブジェクトのタイプによって異なります。例えば; 不変オブジェクトタイプは、呼び出された関数では変更できませんが、可変オブジェクトは更新または再初期化できます。可変変数の更新または再割り当て/再初期化の決定的な違いは、更新された値が呼び出された関数に反映されるのに対し、再初期化された値は反映されないことです。可変変数への新しいオブジェクトの割り当てのスコープは、Pythonの関数に対してローカルです。@ blair-conradが提供する例は、これを理解するのに最適です。

1
古いですが、修正する義務があります。文字列は、値ではなくJavaとC#の両方で参照渡しされます
John

2

例がたまたまオブジェクト指向であるため、次の変更を行って同様の結果を得ることができます。

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.change('variable')
        print(self.variable)

    def change(self, var):
        setattr(self, var, 'Changed')

# o.variable will equal 'Changed'
o = PassByReference()
assert o.variable == 'Changed'

1
これは機能しますが。参照渡しではありません。これは「オブジェクト参照による受け渡し」です。
ビシュワシュミシュラ2018

1

内部的にオブジェクト属性はインスタンスディクショナリに格納されているため、参照オブジェクトを格納するためのインスタンスとして空のクラスを使用するだけで済みます。例を参照してください。

class RefsObj(object):
    "A class which helps to create references to variables."
    pass

...

# an example of usage
def change_ref_var(ref_obj):
    ref_obj.val = 24

ref_obj = RefsObj()
ref_obj.val = 1
print(ref_obj.val) # or print ref_obj.val for python2
change_ref_var(ref_obj)
print(ref_obj.val)

1

C ++などから知られているように参照をシミュレートするアプローチはどこにも言及されていないようなので、「更新」関数を使用して、実際の変数(または「名前」)の代わりにそれを渡します。

def need_to_modify(update):
    update(42) # set new value 42
    # other code

def call_it():
    value = 21
    def update_value(new_value):
        nonlocal value
        value = new_value
    need_to_modify(update_value)
    print(value) # prints 42

これは、「out-only reference」または複数のスレッド/プロセスがある状況で(更新関数のスレッド/マルチプロセッシングを安全にすることにより)主に役立ちます。

明らかに、上記は値の読み取りを許可せず、更新のみを行います。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.