99

一部の言語では、 refvalなどの特別な予約語を使用して、参照または値でパラメーターを渡すことができます。Python関数にパラメーターを渡すと、関数を離れるときにパラメーターの値が変更されることはありません。これを行う唯一の方法は、グローバル予約語を使用することです(または私が現在理解しているように)。

例1:

k = 2

def foo (n):
     n = n * n     #clarity regarding comment below
     square = n
     return square

j = foo(k)
print j
print k

表示されます

>>4
>>2

kが変更されていないことを示しています。

この例では、変数nは変更されません。

例2:

n = 0
def foo():
    global n
    n = n * n
    return n

この例では、変数nが変更されています。

Pythonで関数を呼び出し、パラメーターがグローバルを使用する代わりにまたは参照パラメーターのいずれかであることをPythonに通知する方法はありますか?

次に、Aレベルのケンブリッジ試験では、関数は単一の値を返すのに対し、プロシージャは複数の値を返すと言われています。80年代に、関数にはreturnステートメントがあり、プロシージャにはないことを教えられました。なぜこれが正しくないのですか?

4

12 に答える 12

204

基本的に 3 種類の「関数呼び出し」があります。

  • 値渡し
  • 参照渡し
  • オブジェクト参照渡し

Python は PASS-BY-OBJECT-REFERENCE プログラミング言語です。

まず、変数と変数の値 (オブジェクト) は 2 つの別個のものであることを理解することが重要です。変数はオブジェクトを「指します」。変数はオブジェクトではありません。また:

変数はオブジェクトではありません

例: 次のコード行:

>>> x = []

[]は空のリストです。 は空のリストxを指す変数ですが、xそれ自体は空のリストではありません。

変数 ( x、上記の場合) をボックスと見なし、変数 ( []) の「値」をボックス内のオブジェクトと見なします。

PASS BY OBJECT REFERENCE (Python の場合):

ここでは、「オブジェクト参照は値渡しです。」

def append_one(li):
    li.append(1)
x = [0]
append_one(x)
print x

ここで、ステートメントx = [0]は object を指す変数x(ボックス) を作成します[0]

呼び出される関数で、新しいボックスliが作成されます。中身liは箱の中身と同じxです。両方のボックスに同じオブジェクトが含まれています。つまり、両方の変数がメモリ内の同じオブジェクトを指しています。したがって、 が指すオブジェクトへの変更は、 が指すオブジェクトliにも反映されますx

結論として、上記のプログラムの出力は次のようになります。

[0, 1]

ノート:

変数liが関数で再割り当てされるとli、メモリ内の別のオブジェクトを指します。xただし、以前に指していたメモリ内の同じオブジェクトを指し続けます。

例:

def append_one(li):
    li = [0, 1]
x = [0]
append_one(x)
print x

プログラムの出力は次のようになります。

[0]

参照渡し:

呼び出し元の関数からのボックスは、呼び出された関数に渡されます。暗黙的に、ボックスの内容 (変数の値) が呼び出された関数に渡されます。したがって、呼び出された関数のボックスの内容に対する変更は、呼び出し元の関数に反映されます。

値渡し:

呼び出された関数で新しいボックスが作成され、呼び出し関数からのボックスの内容のコピーが新しいボックスに格納されます。

お役に立てれば。

于 2015-10-11T15:15:25.417 に答える
86

Pythonの関数内で、strやなどの不変オブジェクトを変更することはできませんが、次のようなことはできます。tuple

def foo(y):
  y[0] = y[0]**2

x = [5]
foo(x)
print x[0]  # prints 25

ただし、配列内の特定の要素を常に2乗する必要がない限り、これは奇妙な方法です。

Pythonでは、複数の値を返すこともできるため、参照渡しのユースケースのいくつかはそれほど重要ではないことに注意してください。

def foo(x, y):
   return x**2, y**2

a = 2
b = 3
a, b = foo(a, b)  # a == 4; b == 9

そのような値を返すと、それらはタプルとして返され、次にアンパックされます。

編集: これについて考える別の方法は、Pythonで参照によって変数を明示的に渡すことはできませんが、渡されたオブジェクトのプロパティを変更できることです。私の例(およびその他)では、リストのメンバーを変更できますただし、渡された変数を完全に再割り当てすることはできません。たとえば、次の2つのコードは、似たようなことをするように見えますが、結果は異なります。

def clear_a(x):
  x = []

def clear_b(x):
  while x: x.pop()

z = [1,2,3]
clear_a(z) # z will not be changed
clear_b(z) # z will be emptied
于 2012-11-08T23:13:03.187 に答える
30

わかりました、私はこれを突き刺します。Python はオブジェクト参照によって渡します。これは、通常「参照による」または「値による」と考えられるものとは異なります。次の例を見てください。

def foo(x):
    print x

bar = 'some value'
foo(bar)

したがって、値「some value」を持つ文字列オブジェクトを作成し、それを という名前の変数に「バインド」していますbarbarC では、これは「ある値」へのポインターに似ています。

を呼び出すとfoo(bar)、それ自体は渡されませんbarbar's value: 'some value' へのポインタを渡しています。その時点で、同じ文字列オブジェクトへの 2 つの「ポインター」があります。

それを次のように比較します。

def foo(x):
    x = 'another value'
    print x

bar = 'some value'
foo(bar)

ここに違いがあります。行で:

x = 'another value'

の内容を実際に変更しているわけではありませんx。実際、それは不可能です。代わりに、値「別の値」を持つ新しい文字列オブジェクトを作成しています。その代入演算子?x「指している物を新しい値で上書きせよ」と言っているわけではありません。x「代わりに新しいオブジェクトを指すように更新してください」と言っています。その行の後に、2 つの文字列オブジェクトがあります。'some value' (barポイントしている) と 'another value' (xポイントしている) です。

これは不器用ではありません。それがどのように機能するかを理解すると、美しくエレガントで効率的なシステムになります。

于 2012-11-09T00:35:47.370 に答える
28

次の説明がうまくまとめられていることを願っています。

ここで考慮すべき点が 2 つあります。変数とオブジェクトです。

  1. 変数を渡す場合、それは値渡しです。つまり、関数内の変数に加えられた変更はその関数に対してローカルであるため、グローバルには反映されません。これは「C」に似た動作です。

例:

def changeval( myvar ):
   myvar = 20; 
   print "values inside the function: ", myvar
   return

myvar = 10;
changeval( myvar );
print "values outside the function: ", myvar

O/P:

values inside the function:  20 
values outside the function:  10
  1. リストなどの可変オブジェクト内にパックされた変数を渡す場合、オブジェクトが再割り当てされない限り、オブジェクトに加えられた変更はグローバルに反映されます。

例:

def changelist( mylist ):
   mylist2=['a'];
   mylist.append(mylist2);
   print "values inside the function: ", mylist
   return

mylist = [1,2,3];
changelist( mylist );
print "values outside the function: ", mylist

O/P:

values inside the function:  [1, 2, 3, ['a']]
values outside the function:  [1, 2, 3, ['a']]
  1. 次に、オブジェクトが再割り当てされた場合を考えてみましょう。この場合、オブジェクトは、これが発生する関数に対してローカルな新しいメモリ ロケーションを参照するため、グローバルには反映されません。

例:

def changelist( mylist ):
   mylist=['a'];
   print "values inside the function: ", mylist
   return

mylist = [1,2,3];
changelist( mylist );
print "values outside the function: ", mylist

O/P:

values inside the function:  ['a']
values outside the function:  [1, 2, 3]
于 2013-06-03T09:15:36.703 に答える
10

Python は値渡しでも参照渡しでもありません。here で説明されているように、 「オブジェクト参照は値で渡されます」のほうが多いです。

  1. これが値渡しではない理由です。 なぜなら

    def append(list):
        list.append(1)
    
    list = [0]
    reassign(list)
    append(list)
    

[0,1] を返します。これは、関数が親スコープを変更することをまったく許可しないため、ある種の参照が値渡しとして渡されたことを示しています。

参照渡しのようですね。いいえ。

  1. これが参照渡しではない理由です。なぜなら

    def reassign(list):
      list = [0, 1]
    
    list = [0]
    reassign(list)
    print list
    

リストが再割り当てされたときに元の参照が破棄されたことを示す [0] を返します。 pass-by-reference は [0,1] を返します。

詳細については、こちらをご覧ください。

関数がスコープ外で操作しないようにする場合は、新しいオブジェクトを作成する入力パラメーターのコピーを作成する必要があります。

from copy import copy

def append(list):
  list2 = copy(list)
  list2.append(1)
  print list2

list = [0]
append(list)
print list
于 2015-07-22T18:21:59.890 に答える
10

技術的には、Python は引数を値で渡しません。すべて参照渡しです。しかし... Pythonには不変と可変の2種類のオブジェクトがあるため、次のようになります。

  • 不変の引数は効果的に値で渡されます。文字列、整数、タプルはすべて不変のオブジェクト型です。それらは技術的には「参照によって渡されます」(すべてのパラメーターと同様) ですが、関数内でその場で変更することはできないため、値によって渡されたかのように見え/動作します。

  • 変更可能な引数は実質的に参照によって渡されます。リストまたは辞書はそのポインターによって渡されます。(append または del) のような関数内のインプレース変更は、元のオブジェクトに影響します。

これが Python の設計方法です。コピーはなく、すべてが参照によって渡されます。明示的にコピーを渡すことができます。

def sort(array):
    # do sort
    return array

data = [1, 2, 3]
sort(data[:]) # here you passed a copy

最後に言及したいのは、関数には独自のスコープがあります。

def do_any_stuff_to_these_objects(a, b): 
    a = a * 2 
    del b['last_name']

number = 1 # immutable
hashmap = {'first_name' : 'john', 'last_name': 'legend'} # mutable
do_any_stuff_to_these_objects(number, hashmap) 
print(number) # 1 , oh  it should be 2 ! no a is changed inisde the function scope
print(hashmap) # {'first_name': 'john'}
于 2020-04-01T20:16:37.243 に答える
3

Python は値によってのみ変数を渡しますが、Python のすべての変数は参照であるため、これは微妙な点です。関数呼び出しで値を変更できるようにしたい場合は、可変オブジェクトが必要です。例えば:

l = [0]

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

set_3(l)
print(l[0])

上記のコードでは、関数は List オブジェクト (変更可能) の内容を変更するため、出力は 0 ではなく 3 になります。

この回答は、Python での「値による」の意味を説明するためだけに書いています。上記のコードはスタイルが悪いため、実際に値を変更したい場合は、クラスを作成し、そのクラス内でメソッドを呼び出す必要があります。MPX が提案しているように。

于 2012-11-08T23:13:49.020 に答える
0

与えられた答えは

def set_4(x):
   y = []
   for i in x:
       y.append(i)
   y[0] = 4
   return y

l = [0]

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

set_3(l)
print(l[0])

質問に書かれていることを実行する限り、これが最良の答えです。ただし、VB や Pascal に比べて非常に扱いにくい方法に思えます。これが最善の方法でしょうか?

不器用なだけでなく、元のパラメーターを手動で何らかの方法で変更する必要があります。たとえば、元のパラメーターをリストに変更するか、単に「このパラメーターを値として使用する」または「これを使用する」と言うのではなく、別のリストにコピーします。参考までに」。簡単な答えは、これに対する予約語はありませんが、これらは優れた回避策でしょうか?

于 2012-11-08T23:53:37.747 に答える
-2

Pythonでは、参照または値による受け渡しは、渡す実際のオブジェクトと関係があります。たとえば、リストを渡す場合、リストは変更可能なオブジェクトであるため、実際にはこの参照による受け渡しを行います。したがって、関数へのポインタを渡し、関数本体のオブジェクト(リスト)を変更できます。

文字列を渡す場合、この受け渡しは値によって行われるため、新しい文字列オブジェクトが作成され、関数が終了すると破棄されます。つまり、すべては可変オブジェクトと不変オブジェクトに関係しています。

于 2012-11-08T23:12:48.743 に答える