1

このエッジケースについては、エキスパートPythonプログラミングで読みました。このコードを確認してください:

def f(arg={}):
    arg['3'] = 4
    return arg

>>> print f()
{'3': 4}
>>> res = f()
>>> res['4'] = 'Still here'
>>> print f()
{'3': 4, '4': 'Still here'}

fargに空のdictを割り当てる代わりに(引数なしで呼び出されたため)、前回(戻り値が保存された後)に呼び出されたときに、古い参照が保持される理由はわかりません。

この本は次のように述べています。「引数内にオブジェクトが作成された場合、関数がオブジェクトを返すと、引数参照は引き続き有効です」。

「これがその仕組み」だと理解していますが、なぜそうなのですか?

4

3 に答える 3

2

デフォルトの引数は、関数が評価されて作成されるときに 1 回だけ評価されるためです (これらは関数定義の一部であり、たとえば、inspect.getargspec を介して取得できます)。

それらは関数の一部であるため、関数へのすべての呼び出しはデフォルト値の同じインスタンスを持ちます。これは、不変値であれば問題ありませんが、可変になるとすぐに落とし穴になる可能性があります。

クラス定義が与えられた場合、同じ「機能」がクラス定義に存在します。

class A(object):
    foo = {}

呼び出し

x = A() 
y = A()
x.foo['bar'] = "baz"

... x と y は同じfooを持つため、y.foo['bar'] は "baz" と評価されます。これが、メンバーの初期化をクラス本体ではなくinitで行う必要がある理由です。

于 2012-02-09T17:17:14.043 に答える
2

あなたの問題は、可変引数(この場合は辞書)にデフォルト設定されています:

def f(arg={}):
    arg['3'] = 4
    return arg

次のようにする必要があります。

def f(arg=None):
    arg = arg if arg is not None else {}
    arg['3'] = 4
    return arg

収量:

>>> print f()
{'3': 4}
>>> res = f()
>>> res['4'] = 'Still here'
>>> print f()
{'3': 4}

あなたが期待するように。

ここでの問題は、関数が呼び出されたときではなく、関数が最初に定義/解析されたときにデフォルトの引数が評価されることです。これは、知っておく必要がある python パーサーのニュアンスにすぎません。

その理由については、「Least Astonishment」と Mutable Default Argument を確認してください。

于 2012-02-09T17:06:52.983 に答える
1

デフォルトのパラメーターは、関数が宣言されたときに一度作成されるため、 f() へのすべての呼び出しは、空で始まる辞書の同じインスタンスを取得します。それは質問に答えていますか?

于 2012-02-09T17:14:41.897 に答える