データフレームを関数に渡し、関数内で変更した場合、値渡しですか、参照渡しですか?
私は次のコードを実行します
a = pd.DataFrame({'a':[1,2], 'b':[3,4]})
def letgo(df):
df = df.drop('b',axis=1)
letgo(a)
a
の値は、関数呼び出し後も変わりません。値渡しであることを意味していますか?
私も次のことを試しました
xx = np.array([[1,2], [3,4]])
def letgo2(x):
x[1,1] = 100
def letgo3(x):
x = np.array([[3,3],[3,3]])
letgo2()
はxx
を変更し、letgo3()
は変更しません。なぜこんな感じ?
簡単な答えは、Pythonは常に値渡しを行いますが、すべてのPython変数は実際には何らかのオブジェクトへのポインターなので、参照渡しのように見える場合があります。
Pythonでは、すべてのオブジェクトは可変または不可変です。たとえば、リスト、辞書、モジュール、およびPandasデータフレームは可変であり、int、string、およびtupleは変更不可です。可変オブジェクトは内部で変更できます(たとえば、リストに要素を追加します)が、可変オブジェクトは変更できません。
最初に言ったように、すべてのPython変数をオブジェクトへのポインターと考えることができます。変数を関数に渡すと、関数内の変数(ポインター)は常に渡された変数(ポインター)のコピーです。したがって、内部変数に何か新しいものを割り当てた場合、あなたがしていることは別のオブジェクトを指すローカル変数。これは、変数が指す元のオブジェクトを変更(変更)することも、外部変数が新しいオブジェクトを指すようにすることもありません。この時点で、外部変数は元のオブジェクトをポイントしていますが、内部変数は新しいオブジェクトをポイントしています。
元のオブジェクトを変更したい場合(可変データ型でのみ可能)、オブジェクトを変更する何かを行う必要がありますwithoutローカル変数に完全に新しい値を割り当てます。これがletgo()
とletgo3()
が外部アイテムを変更しないままにする理由ですが、letgo2()
はそれを変更します。
@ursanが指摘したように、letgo()
が代わりにこのようなものを使用すると、df
が指す元のオブジェクトが変更(突然変異)され、グローバルa
変数を介して表示される値が変更されます。
def letgo(df):
df.drop('b', axis=1, inplace=True)
a = pd.DataFrame({'a':[1,2], 'b':[3,4]})
letgo(a) # will alter a
場合によっては、直接割り当てを実際に行うことなく、元の変数を完全に空洞化し、新しいデータで再充填することができます。これにより、v
が指す元のオブジェクトが変更され、後でv
を使用したときに表示されるデータが変更されます。
def letgo3(x):
x[:] = np.array([[3,3],[3,3]])
v = np.empty((2, 2))
letgo3(v) # will alter v
x
に何かを直接割り当てていないことに注意してください。 x
の内部範囲全体に何かを割り当てています。
完全に新しいオブジェクトを作成し、外部から見えるようにする必要がある場合(パンダの場合があります)、2つのオプションがあります。 「クリーン」オプションは、新しいオブジェクトを返すことです。たとえば、
def letgo(df):
df = df.drop('b',axis=1)
return df
a = pd.DataFrame({'a':[1,2], 'b':[3,4]})
a = letgo(a)
別のオプションは、関数の外部に到達し、グローバル変数を直接変更することです。これにより、a
が新しいオブジェクトを指すように変更され、後でa
を参照する関数には、その新しいオブジェクトが表示されます。
def letgo():
global a
a = a.drop('b',axis=1)
a = pd.DataFrame({'a':[1,2], 'b':[3,4]})
letgo() # will alter a!
グローバル変数を直接変更することは、通常は悪い考えです。コードを読み取る人は、a
がどのように変更されたかを理解するのに苦労するからです。 (私は通常、スクリプト内の多くの関数で使用される共有パラメーターにグローバル変数を使用しますが、それらのグローバル変数を変更させません。)
問題は、PBV対PBRではありません。これらの名前は、Pythonのような言語で混乱を引き起こすだけです。 CまたはFortranのような(典型的なPBVおよびPBR言語として)動作する言語のために発明されました。 Pythonは常に値渡しをするのは事実ですが、啓発的ではありません。ここでの質問は、値自体が変異しているかどうか、または新しい値を取得するかどうかです。 Pandasは通常、後者の側でエラーになります。
http://nedbatchelder.com/text/names.html は、Pythonの名前体系が何であるかを非常によく説明しています。
@Mike Grahamの回答に追加するには、誰が非常に良い読み物を指していましたか:
あなたの場合、覚えておくべき重要なことは、namesとvaluesの違いです。 a
、df
、xx
、x
はすべてnamesですが、同じまたは異なるvaluesあなたの例の異なるポイントで:
最初の例では、letgo
rebindsdf
は別の値になります。df.drop
は新しいDataFrame
引数inplace = True
を設定しない限り( docを参照 )。これは、df
の値を参照していた名前letgo
(a
関数のローカル)が新しい値、ここではdf.drop
を参照していることを意味します戻り値。 a
が参照している値はまだ存在し、変更されていません。
2番目の例では、letgo2
mutatesx
、再バインドせずに、xx
が変更される理由letgo2
。前の例とは異なり、ここではローカル名x
は常に名前xx
が参照している値を参照し、その値をインプレースに変更します。 xx
が参照している値が変更された理由です。
3番目の例では、letgo3
rebindsx
を新しいnp.array
に再バインドします。これにより、名前はx
にローカルで、letgo3
に対してローカルで、以前はxx
の値を参照していましたが、新しいnp.array
という別の値を参照するようになります。 xx
が参照している値は変更されていません。
Pythonは、値渡しでも参照渡しでもありません。割り当てによる合格です。
サポート参照、Python FAQ: https://docs.python.org/3/faq/programming.html#how-do-i-write-a-function-with-output -parameters-call-by-reference
IOW:
したがって、リストを渡し、その0番目の値を変更すると、その変更は、呼び出し先と呼び出し元の両方で見られます。ただし、新しいリストでリストを再割り当てすると、この変更は失われます。ただし、リストをスライスしてthatを新しいリストに置き換えると、その変更は呼び出された側と呼び出し元の両方で見られます。
例えば:
def change_it(list_):
# This change would be seen in the caller if we left it alone
list_[0] = 28
# This change is also seen in the caller, and replaces the above
# change
list_[:] = [1, 2]
# This change is not seen in the caller.
# If this were pass by reference, this change too would be seen in
# caller.
list_ = [3, 4]
thing = [10, 20]
change_it(thing)
# here, thing is [1, 2]
あなたがCファンなら、これは値でポインタを渡すと考えることができます-値へのポインタではなく、値へのポインタだけです。
HTH。
関数の開始時に「a」をグローバルにする必要があります。そうでない場合、ローカル変数であり、メインコードの「a」は変更されません。
ドロップのドキュメントは次のとおりです。
要求された軸のラベルが削除された新しいオブジェクトを返します。
したがって、新しいデータフレームが作成されます。オリジナルは変更されていません。
しかし、Pythonのすべてのオブジェクトに関しては、データフレームは参照によって関数に渡されます。