0

重複の可能性:
Pythonの「驚き最小の原則」:可変のデフォルト引数

次の2つの関数を検討してください

def a(var=[0]):
    print (var)
    var = var + [4]

def b(var=[0]):
    print (var)
    var.append(4)

それらは同じように動作する必要がありますが、さらに重要なことに、引数なしで呼び出された場合、両方とも単に「[0]」を出力する必要があります。動作は大きく異なり、a()のみが常に「[0]」を出力します。

>>> a()
[0]
>>> a()
[0]
>>> a()
[0]

>>> b()
[0]
>>> b()
[0, 4]
>>> b()
[0, 4, 4]

a()の機能がb()と異なるのはなぜですか?

関数に引数が渡されていない場合に変数をデフォルトで上書きしたい場合は、リストAPIを使用できないようです。そして、あなたがそうするならば、関数はそれが以前に変数であったものを「覚えている」でしょう。

私のコードの状況は再帰関数に現れるので、不要な変数を「削除」するだけでは実際には機能しません。引数なしで関数を呼び出すたびに変数を上書きする方法はありますか?

何時間もの研究の後で、私はこれを発見しました。上記の質問に関連している可能性があり、回答につながる可能性があります。次のようにライフタイムクラスを定義できます。

class lifetime:   # adapted from http://naml.us/blog/tag/variable-lifetime
    def __init__(self, name):
        self.name = name
        print ('creating: ' + name)
    def __del__(self):
        print ('destroying: ' + self.name)
    def __iadd__(self, a):
        self.append(a)
    def append(self, a):
        self.name += ' and ' + a.name
        print('appending: ' + a.name + ' to ' + self.name)

次に、2つの関数を定義します。

def a(var=lifetime('a')):
    print (var)
    var += life('appendage')

def b(var=lifetime('b')):
    print (var)
    var.append(life('appendage'))

>>> a()
<__main__.lifetime object at 0x00000000031FFA90>
creating: appendage
appending: appendage to a and appendage
destroying: appendage
>>> a()
<__main__.lifetime object at 0x00000000031FFA90>
creating: appendage
appending: appendage to a and appendage and appendage
destroying: appendage
>>> b()
<__main__.lifetime object at 0x00000000031FFB38>
creating: appendage
appending: appendage to b and appendage
destroying: appendage
>>> b()
<__main__.lifetime object at 0x00000000031FFB38>
creating: appendage
appending: appendage to b and appendage and appendage
destroying: appendage

引数のデフォルトを一度評価してから、その評価が何であれ使用するようです。「作成中:a」または「作成中:b」とは決して言いません。したがって、デフォルトの引数を毎回評価すると、実際の質問に対する答えにつながる可能性があります。

4

5 に答える 5

2

あなたが言ったように、これらのデフォルトは、関数が宣言されたときに一度だけ評価されます。そのため、リストまたはハッシュリテラルをデフォルト値として割り当てることは決して良い考えではありません。これは、すべての関数呼び出しに対して1つのインスタンスしか取得できないためです。

その問題はここで説明されています。

于 2012-07-07T19:14:35.903 に答える
1

2つの関数の動作は異なります。これは、最初にローカル変数varを再定義して[0] + [4](で初期化された元のリストに追加しない[0])、2番目の関数では実際に元の初期化リストに追加してからに取得する[0]ため[0, 4]です。そしてあなたが言ったように-デフォルトは一度だけ評価されます。

于 2012-07-07T19:15:50.260 に答える
1

2つのコードスニペットは2つの本質的に異なることを行うためです。var + [4]のリストの内容とが追加された新しいリストを作成し、varこの4新しいリストをに割り当てvarます。var.append(4)一方、既存のリストに番号4を追加します。

問題は、var=[0]一度だけ評価されてから再利用されるappendことです。変数自体を変更すると、後の呼び出しへの影響がわかります。他のコードは、後で効果がないローカル変数に新しいオブジェクトを割り当てるだけです。

通常、変更可能なデフォルトは使用せず、代わりに次のように記述します。

def foo(var=None):
    if var is None: var = [0]

以前の呼び出しからのデータを記憶する必要がある場合は、関数全体をクラスに入れてください。

于 2012-07-07T19:16:22.447 に答える
1

あなたがそれを次のように書くならば、何が起こっているのかを想像する方が簡単かもしれません

t1 = [0]
def a(var=t1):
    print (var)
    var = var + [4]

t2 = [0]
def b(var=t2):
    print (var)
    var.append(4)
于 2012-07-07T19:18:05.243 に答える
0

それは簡単です。最初の関数でa()は、「名前」を古いリストとvarの内容を含む新しいリストに再割り当てします。2番目の関数では、名前が参照しているものを変更せずに実際のオブジェクトにアクセスしています。Pythonの「驚き最小の原則」:可変のデフォルト引数がなぜそのように応答するのかについての説明については、この質問を参照してください。var[4]b()varb()

于 2012-07-07T19:17:50.760 に答える