0

重複の可能性:
Python の「最小の驚き」: 変更可能な既定の引数

Python 2.7 では、次のコードがあるとします。

class Base(object):
    # Variant 1
    def __init__(self, records=[]):
        self._records = records

    # Variant 2
    # def __init__(self, records=[]):
    #     self._records = []
    #     if records:
    #         self._records = records

    def append(self, value):
        self._records.append(value)

class ChildA(Base):
    pass

class ChildB(Base):
    pass

a = ChildA()
b = ChildB()
a.append(100)
b.append(200)

print a._records
print b._records

バリアント 1 を使用して基本クラスを初期化すると、self._records はクラス変数のように動作します。バリアント 1 を使用してコードを実行し、基本クラスを初期化すると、次の出力が得られます。

[100, 200]
[100, 200]

バリアント 2 を使用して基本クラスを初期化すると、self._records はインスタンス変数のように動作します (予想どおり)。バリアント 2 を使用してコードを実行し、基本クラスを初期化すると、次の出力が得られます。

[100]
[200]

これらの両方のバリアントの違いは何ですか? バリアント 1 とバリアント 2 の動作が異なるのはなぜですか? 助けてくれてどうもありがとう!

4

3 に答える 3

1

継承、クラス、またはインスタンス変数とは何の関係もありません。次のコードを検討してください。

>>> def f(a=[]):
...     a.append(1)
...     print a
...
>>> f.func_defaults
([],)
>>> f()
[1]
>>> f()
[1, 1]
>>> f.func_defaults
([1, 1],)

関数パラメーターのデフォルト値は1つだけ評価され、関数オブジェクト内に格納されます。呼び出されるたびfに、同じリストで動作します。あなたの場合と同じように。

于 2012-08-09T09:09:06.637 に答える
1

デフォルトの引数は です[]。これは、Python でよくある落とし穴です。チュートリアルで詳細を参照してください。

重要な警告: デフォルト値は一度だけ評価されます。デフォルトがリスト、ディクショナリ、またはほとんどのクラスのインスタンスなどの可変オブジェクトである場合、これは違いを生みます。

于 2012-08-09T08:58:51.307 に答える
0

他の人が言ったように-これは、クラス継承の動作ではなく、空のリストをデフォルト値として使用することに関係しています。

それを行う正しい方法は次のとおりです。

class Base(object):
    # Variant 1
    def __init__(self, records=None):
        if records is None:
            records = []
        self._records = records

したがって、Base クラスがインスタンス化されるたびに、新しいリスト インスタンスが確実に作成されます。コードに入れる方法では、リスト オブジェクトは、クラス本体が Python によって解析されるときにインスタンス化され、メソッドが実行listされるたびに、その 1 つのインスタンスが既定のパラメーターとして使用されます。__init__オブジェクトが参照を保持し、同じリストを変更すると、Baseクラスを共有する他のすべてのオブジェクトで変更が表示されます。

于 2012-08-09T11:53:02.327 に答える