Pythonでの参照による整数の受け渡し


97

Pythonで参照によって整数を渡すにはどうすればよいですか?

関数に渡す変数の値を変更したい。Pythonのすべてが値渡しであることを読みましたが、簡単なトリックが必要です。たとえば、Javaでは、あなたはの参照型渡すことができIntegerLongなど

  1. 参照によって整数を関数に渡すにはどうすればよいですか?
  2. ベストプラクティスは何ですか?

'参照'のように動作する匿名クラス(可変)でintをラップするための少し複雑な方法については、これを参照してください:stackoverflow.com/a/1123054/409638 ie ref = type( ''、() 、{'n':1})
robert 2014年

回答:


107

Pythonではそのようには機能しません。Pythonはオブジェクトへの参照を渡します。関数内にはオブジェクトがあります-(可能であれば)そのオブジェクトを自由に変更できます。ただし、整数は不変です。回避策の1つは、変更可能なコンテナーに整数を渡すことです。

def change(x):
    x[0] = 3

x = [1]
change(x)
print x

これはせいぜい醜い/不器用ですが、Pythonでこれ以上うまくいくことはありません。その理由は、Pythonでは、代入(=)が右側の結果であるオブジェクトを受け取り、それを左側にあるものにバインドするためです*(または適切な関数に渡します)。

これを理解すると、関数内の不変オブジェクトの値を変更する方法がない理由がわかります。不変であるため、その属性を変更することはできず、「変数」に新しいものを割り当てることはできません。これは、実際に新しいオブジェクト(古いオブジェクトとは異なる)を作成し、古いオブジェクトがローカル名前空間で持っていた名前を付けているためです。

通常、回避策は、必要なオブジェクトを返すことです。

def multiply_by_2(x):
    return 2*x

x = 1
x = multiply_by_2(x)

*上記の最初の例では、3実際にはに渡されx.__setitem__ます。


11
「Pythonはオブジェクトを渡します。」いいえ。Pythonは参照(オブジェクトへのポインター)を渡します。
user102008 2013

6
@ user102008-フェアポイント。この時点でのセマンティクスは非常に泥だらけになります。最終的には、それらも「ポインタ」ではありません。少なくとも、確かにあなたが持っているのと同じ意味ではありませんC。(たとえば、逆参照する必要はありません)。私のメンタルモデルでは、物事を「名前」や「オブジェクト」と考える方が簡単です。割り当ては、左側の「名前」を右側の「オブジェクト」にバインドします。関数を呼び出すときは、「オブジェクト」を渡します(関数内の新しいローカル名に効果的にバインドします)。
mgilson 2013

これはもちろんPythonの参照が何であるかについての説明ですが、用語に精通していない人に簡潔に説明する方法を見つけるのは簡単ではありません。
mgilson 2013

2
用語と混同されるのも不思議ではありません。ここでは、オブジェクトごとの呼び出しとして説明し、値ごとのパスとして説明しています。それ以外の場所では、実際の意味にアスタリスクが付いた「参照渡し」と呼ばれています...基本的に、問題は、コミュニティがそれを何と呼ぶか​​を
mgilson 2013

1
PythonリファレンスはJavaリファレンスとまったく同じです。また、Java参照は、オブジェクトへのJLSポインターに従います。そして、基本的に、Java / Python参照とセマンティクスのオブジェクトへのC ++ポインターの間には完全な全単射があり、このセマンティクスについても説明しているものは他にありません。「逆参照する必要はありません」そうですね、.演算子は単に左側を逆参照します。演算子は言語によって異なる場合があります。
user102008 2013

31

参照渡しが必要になるほとんどの場合は、複数の値を呼び出し元に返す必要がある場合です。「ベストプラクティス」は、複数の戻り値を使用することです。これは、Javaなどの言語よりもPythonで行う方がはるかに簡単です。

簡単な例を次に示します。

def RectToPolar(x, y):
    r = (x ** 2 + y ** 2) ** 0.5
    theta = math.atan2(y, x)
    return r, theta # return 2 things at once

r, theta = RectToPolar(3, 4) # assign 2 things at once

9

値を直接渡すのではなく、渡されたかのように使用します。

x = 7
def my_method():
    nonlocal x
    x += 1
my_method()
print(x) # 8

警告:

  • nonlocal Python3で導入されました
  • 囲んでいるスコープがグローバルスコープの場合は、のglobal代わりにを使用しますnonlocal

7

実際、ベストプラクティスは、一歩下がって、本当にこれを行う必要があるかどうかを尋ねることです。関数に渡す変数の値を変更したいのはなぜですか?

簡単なハックのためにそれを行う必要がある場合、最も簡単な方法はlist、整数を保持することを渡し、[0]mgilsonの答えが示すように、それを使用するたびにそれを回避することです。

より重要なことのためにそれを行う必要がある場合は、属性としてclassを持っているintを記述して、それを設定するだけです。もちろん、これにより、クラスと属性の適切な名前を思い付く必要があります。何も考えられない場合は、戻って文を数回読み直してから、を使用してくださいlist

より一般的には、JavaイディオムをPythonに直接移植しようとしている場合、それは間違っています。(static/のように@staticmethod)直接対応するものがある場合でも、Javaで使用するという理由だけで、ほとんどのPythonプログラムでそれを使用することは望ましくありません。


JAVAは参照によって整数を渡しません(整数または任意のオブジェクト。ボックス化されたオブジェクトも割り当て時に置き換えられます)。
ルイスマスエリ2014年

@LuisMasuelliええと、ボックス化されたプリミティブはオブジェクトと同じように扱われます。OPが望むようにそれらの使用を妨げる唯一のことは、ボックスが不変であるという事実です(そして、プリミティブ自体も不変であるため、全体が不変であり、でのみ変更できます。可変レベル)
Kroltan 2017

これの良いユースケースは、再帰関数内の呼び出しの数(深さではなく合計)をカウントすることです。すべての分岐呼び出しでインクリメントできる番号が必要です。標準のintはちょうどそれをカットしていません
z33k

4

Pythonでは、Javaの非プリミティブと同様に、すべての値は参照(オブジェクトへのポインター)です。また、Javaと同様に、Pythonには値渡しのみがあります。したがって、意味的には、それらはほとんど同じです。

質問の中でJavaについて言及されているので、Javaで必要なことをどのように達成するかを見てみたいと思います。Javaで表示できる場合は、Pythonでまったく同じように表示する方法を紹介できます。


4

numpyの単一要素アレイは可変とまだほとんどの目的のためであり、それは数値Pythonの変数であるかのように、それを評価することができます。したがって、単一要素リストよりも便利な参照番号コンテナです。

    import numpy as np
    def triple_var_by_ref(x):
        x[0]=x[0]*3
    a=np.array([2])
    triple_var_by_ref(a)
    print(a+1)

出力:

3

3
class PassByReference:
    def Change(self, var):
        self.a = var
        print(self.a)
s=PassByReference()
s.Change(5)     

3

多分それはpythonicな方法ではありませんが、あなたはこれを行うことができます

import ctypes

def incr(a):
    a += 1

x = ctypes.c_int(1) # create c-var
incr(ctypes.ctypes.byref(x)) # passing by ref

頭のいい男。
xilpex


0

Pythonでは、すべてが値によって渡されますが、ある状態を変更する場合は、メソッドに渡されるリストまたはオブジェクト内の整数の値を変更できます。


3
everything is passed by value本当ではないからだと思います。引用ドキュメント:arguments are passed using call by value (where the value is always an object reference, not the value of the object)
アステリー2016

1
リスト、オブジェクト、および辞書は参照によって渡されます
AN88 2017

2
それが当てはまる場合、関数内のパラメーターへの割り当ては、呼び出しサイトに反映されます。Asteryが引用した受け渡しは値によるものであり、これらの値はオブジェクト参照です。
再帰的

0

正解は、クラスを使用して値をクラス内に配置することです。これにより、必要に応じて参照渡しを行うことができます。

class Thing:
  def __init__(self,a):
    self.a = a
def dosomething(ref)
  ref.a += 1

t = Thing(3)
dosomething(t)
print("T is now",t.a)

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