何が起こっているかを把握する最も簡単な方法は、視覚的な表現を使用することだと思います (この表現のアイデアは私のものではありませんが、私は気に入っています)。
まず第一に、Python にはオブジェクトへの参照しかないことを理解する必要があります。オブジェクト自体は、互いに別々に存在します。たとえば、 listは、 objectおよびobject への参照[0, 1]
を含む list-objectです。参照はある種のリンクです。これは、他の言語の変数とは異なります。変数は一般に、物を置くメモリの場所だからです。Python では、「変数」、つまり識別子は、単にオブジェクトの「名前」(=参照) です。0
1
これを理解するために、比喩を使って物体間の関係を想像してみましょう。物体は海にある重い岩で、ロープとフック (¿) でつながれているとしましょう。海の表面には、オブジェクトを参照する識別子が住んでいます。識別子は、オブジェクトが深みに沈むのを防ぐブイです(海の怪物(別名ガベージコレクター)がそれらを破壊すると彼らは言います)。
たとえば、この状況を表すことができます。
a = [0, 1]
次の図を使用します。
___
( )
~~~~~~~~( a )~~~~~~~~
(___)
o ¿ o
| O
| o
|
|
+------+-------+
| [ ¿ , ¿ ] |
+----|-----|---+
| |
| |
o | |
O | |
| |
+-+-+ +-+-+
| 0 | | 1 |
+---+ +---+
o O o
)
( ) o
) )( ) ( (
( ( )( ( ( ) )
ご覧のとおり、識別子a
はリスト オブジェクトを参照しています。つまり、ロープでリンクされています。list-object には 2 つのスロットがあり、それぞれにオブジェクト0
およびに接続されたリンクが含まれています1
。
さて、もしそうなら:
b = a
識別子はの同じオブジェクトをb
参照します:a
___ ___
( ) ( )
~~~~~~~~~~~( a )~~~~~~~~~~~~~~~( b )~~~~~~~~~~~~~~~~
(___) (___)
¿ ¿
\ /
o \ / o
o \ / o
-------+-------
O | [ ¿ , ¿ ] | O
----|-----|----
| |
+-+-+ +-+-+
o | 0 | | 1 |
+---+ +---+ o
O
o O
o
)
) ( ) (
( ( )( ( ( )
( ) ) ( ) ( ( ) ) ( )
代わりに、次の方法で の浅いコピーを行う場合a
:
b = a[:]
新しいリストが作成され、その要素はによって参照されるオブジェクトへの参照のコピーです。つまり、ロープのコピーを作成しましたが、それらは同じ要素を指しています。a
___ ___
( ) ( )
~~~~~~~~~~~( a )~~~~~~~~~~~~~~~( b )~~~~~~~~~~~~~~~~
(___) (___)
O ¿ ¿ o
| |
o | |
| |
-------+------ ------+-------
| [ ¿ , ¿ ] | | [ ¿ , ¿ ] |
----|----|---- ----|----|----
| | | |
\ \ / /
\ \ / /
\ \ / / o
o \ \ / / o
\ \ / / o
o \ \ / /
\ \ / / o
O \ X /
\ / \ /
\/ \/
| |
| |
| |
+-+-+ +-+-+
| 0 | | 1 |
+---+ +---+
)
( ( ) (
)( ) ) ) ( ( ) ) )
( ) ( ) ( ( ( ( ) ) ( ) ( ( (
整数は不変であるため、コピーまたは同じ同一のオブジェクトを使用することに違いはありませんが、整数をmutablelist
である s に置き換えると、同じオブジェクトへの参照が変更されるため、表示される動作になります。
視覚的に、コード:
a = [[0, 1], [0, 1]]
b = a[:]
結果:
___ ___
( ) ( )
~~~~~~~~~~~( a )~~~~~~~~~~~~~~~( b )~~~~~~~~~~~~~~~~
(___) (___)
O ¿ ¿ o
| |
o | |
| |
-------+------ ------+-------
| [ ¿ , ¿ ] | | [ ¿ , ¿ ] |
----|----|---- ----|----|----
| \ / |
| \ / |
| \ / |
| \ / |
| \ / |
| \ / |
| \ / |
| X |
| / \ |
| / \ |
| / \ |
| / \ |
| / \ |
| / \ |
| | \ |
| | | |
+----+-----+----+ +-----+----+----+
| [ ¿ , ¿ ] | | [ ¿ , ¿ ] |
+----|-----|----+ +----|-----|----+
\ \ / /
\ \ / /
\ \ / /
\ \ / /
\ \ / /
\ | / /
| |/ /
| X /
| / | /
| / | /
\ / \ /
Y Y
| |
+-+-+ +-+-+
| 0 | | 1 |
+---+ +---+
)
( ( ) (
)( ) ) ) ( ( ) ) )
( ) ( ) ( ( ( ( ) ) ( ) ( ( (
b
リストが の同じサブリストを参照する方法に注意してくださいa
。(実装の詳細: CPython のバイトコード コンパイラはリテラル式を最適化し、両方のサブリストで同じオブジェクトが使用されるよう0
に1
します。小さな整数にはキャッシュも含まれますが、これは重要ではありません。一般的なケースでは、サブリストにすべての共通の要素)。
ディープ コピーは、この同一オブジェクトの共有を回避するコピーです。
たとえば、実行後:
import copy
a = [[0, 1], [0, 1]]
b = copy.deepcopy(a)
状況は次のとおりです。
___ ___
( ) ( )
~~~~~~~~~~~( a )~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~( b )~~~~~~~~~~~~~~~~
(___) (___)
O ¿ ¿ o
| |
o | |
| |
-------+------ -------+------
| [ ¿ , ¿ ] | | [ ¿ , ¿ ] |
----|----|---- ----|----|----
| \ | \
| \ | \
| \ | \
| \ | \
| \ | \
| \ | \
| \ | \
| \ | \
| \ | \
| \ | \
| \ | \
| \ | \
+----+----------+ +--+------------+ +----+----------+ +--+------------+
| [ ¿ , ¿ ] | | [ ¿ , ¿ ] | | [ ¿ , ¿ ] | | [ ¿ , ¿ ] |
+----|-----|----+ +----|-----|----+ +----|-----|----+ +----|-----|----+
\ \ / / \ \ / /
\ \ / / \ \ / /
\ \ / / \ \ / /
\ \ / / \ \ / /
\ \ / / \ \ / /
\ | / / \ | / /
| |/ / | |/ /
| X / | X /
| / | / | / | /
| / | / | / | /
\ / \ / \ / \ /
Y Y Y Y
| | | |
+-+-+ +-+-+ +-+-+ +-+-+
| 0 | | 1 | | 0 | | 1 |
+---+ +---+ +---+ +---+
) )
( ( ) ( ( ( ) (
)( ) ) ) ( ( ) ) ) )( ) ) ) ( ( ) ) )
( ) ( ) ( ( ( ( ) ) ( ) ( ( ( ( ) ( ) ( ( ( ( ) ) ( ) ( ( (
(実際には、不変オブジェクトの、、copy.deepcopy
などの不変な組み込みオブジェクトをコピーするのを避けるのに十分賢いようです。そのため、すべてのサブリストは同じオブジェクトとオブジェクトを共有します)int
long
tuple
0
1
これらの図は、参照カウントがどのように機能するかを理解するのにも役立つことに注意してください。すべてのロープは参照であり、オブジェクトがブイ (つまり識別子) に到達する参照のチェーンを持つまで、オブジェクトは生き続けます。オブジェクトをサーフェスのブイにリンクするロープがなくなると、オブジェクトは沈み、ガベージ コレクターによって破壊されます。