ホームページ >バックエンド開発 >Python チュートリアル >Pythonのシャローコピー、ディープコピー、参照の仕組みを詳しく解説
今週いくつかの問題に遭遇しましたが、しばらく基礎知識が定着していない場合、まだ復習する必要がある忘れられた部分があることに気づきましたので、記録するためにここにメモしておきます。前
まず、私が遭遇したことを簡単に説明します この質問では、2 つの出力の結果を書く必要があります
ご覧のとおり、Python では、このような代入ステートメントは、実際には a がリスト オブジェクトを指すことを意味します。リストのメモリアドレスはポインタと同様の概念とみなすことができます。
そして b、a オブジェクトをリストにラップして 5 倍することに注意してください。そのため、b はその中にすべての要素を含む大きなリストのように見えるはずです。 a
そして、 a オブジェクトが追加操作を実行すると、実際、暗黙の意味は、メモリ内のリストが変更され、このオブジェクトを参照するすべてのオブジェクトが変更されるということです
a の ID を出力し、同時にオブジェクト b に含まれる要素を出力します。したがって、リスト b では、各要素の ID は a と同じであることがわかります
b a がラップされていますが、a オブジェクトの ID (メモリ アドレス) は 10892296 であることがわかります。新しいリストに追加されましたが、この要素は同じアドレスを持つオブジェクトを参照しています。これは次の図で説明できます
その後、リストは変数オブジェクトであるため、 、メモリ アドレスは変更されていませんが、メモリ内のこのアドレスによって参照されるすべてのオブジェクトが一緒に変更されることが、上のテスト チャートの分割線の下半分からわかります
これは Python への参照につながります。浅いコピーと深いコピーの仕組みとレビュー
Pythonの参照機構
参照機構のケース1
上記の例から、Pythonの参照受け渡し、最終結果は2つのオブジェクトを渡すことになることがわかります。すべてはメモリ内の同じ領域の内容を参照しますそれでは、次の例を見てみましょう
BはAを通過し、IDが17446024のアドレスの内容も参照します。 ID(メモリアドレス)
したがって、 A A[0]=3 または A[3].append(6) の操作を通じて、このメモリの内容は変更されます (list は変数オブジェクトであるため、メモリアドレスは変更されません。これについては後ほど説明します)
これは最も基本的なリファレンスケースです (さらに、A と B は両方とも同じメモリアドレスを指しているため、B によって変更された内容は A にも反映されます)
参照メカニズムのケース 2
別のケースを見てみましょう
質問を見ると、要素 2 がリスト自体に置き換えられるようです。結果は A=[1,[1, 2,3], 3]
しかし、そうではありません! !赤いボックスの真ん中に無限の巣があることがわかります
これはなぜですか?
実際には、A がリスト [1,2,3] を指しているため、A の 2 番目の要素だけが A オブジェクト自体を指しているため、A の構造だけが変更されています。ただし、A はまだそのオブジェクトを指しています
A の ID を出力すると、その指している点が変わっていないことがわかります。 !
見てみましょう、A の方向は変わっていません
それでは、[1,[1,2,3],3] の最終的な出力効果を達成したい場合、どうすればよいでしょうか操作しますか?
ここでは浅いコピーを使用します
浅いコピーと深いコピー
浅いコピー
ここで、浅いコピーと深いコピーについて説明します。上記のメソッドは、実際には浅いコピー、浅いコピーのみを実行します。これは、元の参照オブジェクトをコピーしますが、同じオブジェクトのアドレスを参照することはありません。
以下の例を参照してください。 , B は、B = A[:] 操作を通じてシャロー コピーを実行します。シャロー コピー後、A と B が参照するメモリ アドレスはすでに異なっていることがわかります
ただし、A と B の内部アドレスの参照アドレスは異なります。要素の内容は依然として同じなので、これには十分注意してください。違いがあります! ! !
AとBの参照メモリアドレスの違いは、Bに対して実行する操作はAに影響を与えないことを意味します。
浅いコピーの概要:
つまり、浅いコピーは参照のコピーとして要約できます。新しいオブジェクトと元のオブジェクト参照は区別されますが、内部要素のアドレス参照は同じです
。浅いコピーも問題を引き起こす可能性があります。何が問題ですか?たとえば、次のような状況で、A の浅いコピーを B に割り当て、A と B の ID (メモリ アドレス) が異なる場合です。
つまり、A[0]=8を変更しても、AとBは独立した参照なのでBは影響を受けませんが、中央にネストされたリスト[4,5,6]があります
これは理解できます[4,5,6] の場合: A と B は依然として一緒に参照します。つまり、A と B の 2 番目の要素については、依然として同じメモリ アドレスを指します。
また、intは不変型なので、A[0]を8に変更すると参照アドレスも変わります!要素B[0]の参照とは区別されます。
ディープコピー
それでは、そのような状況にどのように直面すればよいでしょうか? Python モジュールで copy モジュールを使用する必要があります
copy モジュールには 2 つの関数があります
1: copy.copy (コピーしたいオブジェクト): これは浅いコピーであり、前の [:] 操作はlist プロパティは同じです
2: copy.deepcopy (コピーしたいオブジェクト): これは浅いコピーと同じであることに加えて、オブジェクトへの新しい参照を生成します。さらに、内部要素に対して新しい参照が生成され、独立して分離されます。以下の例を見てください。A のディープ コピーを B に割り当てると、それらは不変要素を変更するかどうかに関係なく、完全に独立していると言えます。 A の要素または A を変更する 内部のネストされた変数要素の結果は B には影響しません
私の理解では、ディープコピーは再帰コピーと呼ばれ、すべてのネストされた変数要素をコピーし、それらを個別に参照します
。
ディープコピーの概要:
ディープコピーの効果は、浅いコピーのようにオブジェクトの参照から新しい参照を生成することに加えて、Open.内部のネストされた要素をすべて分離するのに役立ちます。
私は2枚の絵を描きました。浅いコピーと深いコピーの効果の違いを示すためです
浅いコピーの後でも、リスト内の不変要素の参照アドレスは同じままですが、不変要素であるため、したがって、不変要素が変更された後、参照アドレスは元の参照アドレスに影響を与えることなく新しいものになります。
まとめ
ということで、ここでシャローコピーとディープコピーの仕組みが基本的に理解できました。
説明が必要な特殊な状況もあります
不変型: int、str、tuple、float およびその他の要素の場合、それらが変更された後、参照アドレスは次のように直接変更されます。以下
ただし、不変型の中にネストされた可変型がある場合でも、ディープ コピーを使用できます
また注意してください、最も一般的に使用される方法は、次の例のような直接代入 (または参照の直接転送) です
これは、2 つの変数要素 a と b を同時にメモリ アドレスにポイントするため、任意の変更は a と b に影響します
最後に
変数の型: list、set、dict
不変の型: int、str、float、tuple
浅いコピーメソッド: [ :] 、 copy.copy() 、ファクトリ関数(list/dir/set)を使用します
ディープコピーメソッド: copy.deepcopy()
以上がPythonのシャローコピー、ディープコピー、参照の仕組みを詳しく解説の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。