新しいオブジェクトを作成すると、そのクラスの*すべての*インスタンスのフィールドが変更される可能性があるのはなぜですか?

ニール

問題のコードの簡略版は次のとおりです。

class thing:
    def __init__(self, data1, data2={'foo': 1}):
        self.data2 = data2
        self.data2['bar'] = data1

datas = ['FIRST', 'SECOND']
things = [thing(x) for x in datas]
for p in things:
    print p.data2['bar']

私はこのコードが戻ることを期待します:

FIRST
SECOND

ただし、実際には次の値が返されます。

SECOND
SECOND

どうして?


私の最善の推測は:

  • 私が単一の辞書を作成していることur_dict = {'foo': 1}
  • 私はクラスのオブジェクトを初期化するときにthing私はいない新しい辞書を作成するself.data2={'foo': 1}のではなくへの参照を初期化しur_dict
  • そして、コンストラクターでキーと値のペアbar: data1self.data2に追加すると、実際にはそのキーと値がur_dictそれ自体に追加されますこれによりdata2、リスト内のすべてのオブジェクトのフィールドが更新されます。

上記のコードに次のスニペットを追加して、この仮説をテストしました。

things[0].data2['bar'] = 'FIRST'
for p in things:
    print p.data2['bar']

案の定、結果は次のとおりです。

FIRST
FIRST

ですから、私の仮説はおそらく正しいと思いますが、それでも私には不思議です。私の質問はこの質問に非常に関連しているようです---コンストラクターでデフォルト値を作成するとき、この値はインスタンス変数ではなくクラス変数ですか?Pythonがオブジェクトの引数のデフォルト値に対して新しいオブジェクトを作成しないのはなぜですか?将来、この種のエラーを概念化またはキャッチするための良い方法はありますか?ここで何が起こっているかについて学ぶためのいくつかの良い参考資料は何ですか?

(専門用語を壊した場合は事前にお詫びします。私は正式な教育を受けた数学者です。)


編集:@CrazyPython[::-1]これは関数のプロパティであり、クラスやオブジェクトではないと述べました。可変のデフォルト引数を持つ関数の例を作成し、上記のオブジェクトとクラスで経験した方法で関数を壊す方法を見つけようとしています。これを思いついた:

EXPONENT = 1
def fooBar(x, y=EXPONENT):
    return x**y
print EXPONENT
print foo(5)
EXPONENT = 2
print EXPONENT
print foo(5)

ただし、次の値が返されます。

1
5
2
5

ここで変更可能なデフォルト引数を使用しない理由を説明するために、これを「破る」良い方法は何ですか?

noɥʇʎǀʎzɐɹƆ

self.data2={'foo': 1}変更可能なデフォルト引数です。絶対にしないでください。99%の確率でそれは間違いです。

def foo(a, b={}):
    ...

と同等ではありません

def foo(a, b=None):
    if b is None:
        b = {}

b毎回再構築されるわけではありません。それは同じオブジェクトです。

将来、この種のエラーを概念化またはキャッチするための良い方法はありますか?

PyCharmまたは別の優れたエディターを使用する

コンストラクターでデフォルト値を作成するとき、この値はインスタンス変数ではなくクラス変数ですか?

いやいやいやいやいや。これは、クラスやインスタンスではなく、関数に関連付けられています。これは、すべての機能に適用される動作です。クラスやリンクされた質問とは何の関係もありません。

Pythonがオブジェクトの引数のデフォルト値に対して新しいオブジェクトを作成しないのはなぜですか?

パフォーマンスと互換性。オブジェクトの構築は、潜在的にコストのかかる操作です。


補遺

  • クラスにはキャメルケースを使用してください。そうしないと、Pythonの公式スタイルガイドであるPEP8違反になります。
  • まともなエディターを入手してください。個人的には、PyCharmをお勧めします。(私は営業担当者ではありません)無料です。
    • PyCharmは、変数の名前の変更、PEP 8エラーの強調表示、高度なオートコンプリートの実行、型チェック、可変のデフォルト引数の特定などを行うことができます。
  • を継承していませんobjectそれは良くないね。*

    このPythonコードがリスト内すべてのオブジェクトのフィールドを変更するのはなぜですか?

  • 文脈がなければ、それは悪いタイトルです。コードについて尋ねます。タイトルを読んだとき、コードはありません。


より明確にする

関数が指定されると、デフォルトの引数が評価されます。(実行されたときではなく、記述されたとき)それらは関数に関連付けられています。関数が実行されると、デフォルトの引数のコピーは作成されません。デフォルトの引数を直接変更します。呼び出される次の関数は、変更を加えた同じオブジェクトを受け取ります。

※あなたは教授等のようです。ミームに腹を立てないでください。それはインターネットからアドバイスを得るリスクです。

この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。

侵害の場合は、連絡してください[email protected]

編集
0

コメントを追加

0

関連記事

Related 関連記事

ホットタグ

アーカイブ