1

デフォルトのパラメータを持つ関数について質問があります。

import sys
from random import randint

my_list = [1,2,3]
def Veli(a, b = my_list):
    my_list.append(randint(1,1500))
    print a + b[-1]

print "Number of Refs:", sys.getrefcount(my_list)
print "Def args:", Veli.func_defaults
Veli(1) # This is 4
Veli(1) # We almost always obtain different result because of randint
Veli(1) # Again different results.
print Veli.func_defaults
print "Number of Refs:", sys.getrefcount(my_list)
Veli(1)
my_list = [-5] # old my_list has different address 
print "Number of Refs:", sys.getrefcount(my_list) # Less than previous
print "Def args:", Veli.func_defaults
Veli(1) # Now gives same results.
print "Def args:", Veli.func_defaults
Veli(1) # Same result again...


出力:(もちろん、いくつかの数値は、randintが返した値によって異なります。)

参照数:3
Def args:([1、2、3]、)
322
1119
740
([1、2、3、321、1118、739]、)参照
数:3
303参照
数:2
Def args :([1、2、3、321、1118、739、302]、)
303
Def args:([1、2、3、321、1118、739、302]、)
303


次のコードは、bとmy_listが同じアドレスを参照しなくなったため、前のコードよりも少ない数になります。

print "#ref:", sys.getrefcount(my_list) # Less than previous

これで、関数のデフォルトの引数であるbに到達する方法(唯一の方法は?)があります。

Veli.func_defaults[0]


長い説明でごめんなさい。これが私の質問です:

  1. これは問題ですか?私のモジュールのディクショナリはmy_list変数をクラッシュさせ、my_list変数は以前よりもデフォルトのアドレスを持つようになりました。次に、my_listという名前のグローバル変数を本体で使用する関数が変更されます(成長します)が、デフォルトの引数は変更されません。式の実行時に名前がb付けられたグローバル変数が表示されないのはなぜですか?私は知っています、my_listとは異なるアドレスを持っています(相互オブジェクト(listなど)は異なる、一意の、新しく作成されたリストを参照することが保証されているため)が、bが関数の引数であるときにグローバル変数を表示できないようにPythonが実装されたのはなぜですか?包括的に説明していただけますか?my_lista + b[-1]bb

  2. Veli.func_defaults [0]で同じ結果を得る方法はありますか?このコードを実行してから、Veliという名前の関数のデフォルトの引数を変更したいとします。my_listとbのアドレスが異なるため、my_listではこれを行うことができません。1つの方法は、リストの要素Veli.func_defaults[0]を変更することです。別の方法はありますか?

  3. (上記のコードとはあまり関係ありません)変数のアドレスを取得するにはどうすればよいですか?たとえば、?のアドレスを取得するにはどうすればよいbですか?などの組み込み関数を使用して__hash__いますが、もっと適切な方法があるはずです。


ノート:

a)このコードはどういうわけか役に立たないかもしれませんが、私は意見を学びたいです。
b)Python 2.6.6(r266:84292、2010年9月15日、15:52:39)linux2
上の[GCC 4.4.5]

4

2 に答える 2

1
  1. デフォルトの引数は、関数定義時の境界です。この行は、そのときにたまたまdef Veli(a, b = my_list):参照していたオブジェクトへの参照を に入れます。Pythonは参照渡しを使用しません。変数は参照を保持し、それらの参照は常に値渡しされます。したがって、実際に に保存されている参照 (ポインター) はコピーされ、誰もそれがどこから来たのか覚えていないか、わざわざ更新する必要がありません。my_listfunc_defaultsb
  2. あなたが何を求めているのかよくわかりません。明確にしてください。b2 番目の引数が渡されなかった場合は になりVeli.func_defaults[0]ますが、渡された場合は明らかに異なります (もちろん、呼び出し元がアクセスしない限りfunc_defaults... 彼はアクセスしないと想定する必要があります)。別の回答で示唆されているように、bデフォルトを作成Noneしてグローバルmy_listifを使用b is Noneできます-これにより、呼び出しごとにその参照の最新の更新されたコピーが得られます。おそらく、クラスを作成し、デフォルト値を属性として保持しself、通常の(... = None): if ... is None: use = defaultイディオムを適用する必要があります。
  3. 組み込み関数id。実際には、オブジェクトのアイデンティティを表す整数である限り、これが返すものを定義した実装です (アドレスである必要はありません) ididその寿命の間。CPython が選択する簡単な戻り値はアドレスです。
于 2011-02-04T14:51:34.163 に答える
1

あなたの質問は本質的に同じ問題に関連しているため、この既存の質問を見ることをお勧めします(ただし、トピックに関する視点は少し異なります)。

お気づきのように、関数定義が実行されると、bは を指すようにバインドされmy_listます。後でmy_list別のオブジェクトを指すように変更しても、b影響を受けません。

「遅延バインディング」を取得するには、次のように関数を記述します。

def Veli(a, b=None):
    if b is None:
        b = my_list # Gets value of my_list at call time
    my_list.append(randint(1,1500))
    print a + b[-1]
于 2011-02-04T14:54:49.587 に答える