18

Pythonの公式ドキュメントによると、Python でスライス演算子と代入を使用すると、スライスされたリストの浅いコピーが作成されます。

しかし、たとえばコードを書くとき:

o = [1, 2, 4, 5]
p = o[:]

そして、私が書くとき:

id(o)
id(p)

私は異なるIDを取得し、1つのリストを追加しても他のリストには反映されません。ディープコピーを作成していませんか、それともどこか間違っていますか?

4

2 に答える 2

31

ネストされた値はコピーされず、単に参照されるため、浅いコピーを作成しています。ディープコピーは、リストによって参照される値のコピーも作成します。

デモ:

>>> lst = [{}]
>>> lst_copy = lst[:]
>>> lst_copy[0]['foo'] = 'bar'
>>> lst_copy.append(42)
>>> lst
[{'foo': 'bar'}]
>>> id(lst) == id(lst_copy)
False
>>> id(lst[0]) == id(lst_copy[0])
True

ここでは、ネストされた辞書はコピーされません。両方のリストから参照されるだけです。新しい要素42は共有されません。

Python ではすべてがオブジェクトであり、名前とリスト要素はそれらのオブジェクトへの単なる参照であることを思い出してください。リストのコピーは新しい外部リストを作成しますが、新しいリストはまったく同じオブジェクトへの参照を受け取るだけです。

適切なディープ コピーは、リストに含まれるすべてのオブジェクトの新しいコピーを再帰的に作成します。

>>> from copy import deepcopy
>>> lst_deepcopy = deepcopy(lst)
>>> id(lst_deepcopy[0]) == id(lst[0])
False
于 2013-09-28T15:48:51.763 に答える
6

isorを使用したテストは、不変オブジェクトを含む文字列、整数、タプルなどの不変オブジェクトおよびインターンされidたオブジェクトで真のコピーが作成されているかどうかについて、誤解を招く可能性があることを知っておく必要があります。

インターンされた文字列のわかりやすい例を考えてみましょう。

>>> l1=['one']
>>> l2=['one']
>>> l1 is l2
False
>>> l1[0] is l2[0]
True

l1次に、不変文字列の浅いコピーを作成してテストします。

>>> l3=l1[:]
>>> l3 is l1
False
>>> l3[0] is l1[0]
True

に含まれる文字列のコピーを作成しますl1[0]

>>> s1=l1[0][:]
>>> s1
'one'
>>> s1 is l1[0] is l2[0] is l3[0]
True               # they are all the same object

すべての要素をコピーする必要がある場所でディープコピーを試してください。

>>> from copy import deepcopy
>>> l4=deepcopy(l1)
>>> l4[0] is l1[0]
True

いずれの場合も、文字列'one'は Python の不変文字列の内部キャッシュにインターンされておりis、それらが同じであることを示します (同じ を持っていますid)。何がインターンされるか、いつインターンされるかは実装とバージョンに依存するため、依存することはできません。これにより、メモリとパフォーマンスが大幅に向上します。

すぐにインターンしない例を強制できます。

>>> s2=''.join(c for c in 'one')
>>> s2==l1[0]
True
>>> s2 is l1[0]
False

次に、Python のintern 関数を使用して、キャッシュされたオブジェクトが見つかった場合にその文字列を参照させることができます。

>>> l1[0] is s2
False
>>> s2=intern(s2)
>>> l1[0] is s2
True

同じことが不変のタプルにも当てはまります。

>>> t1=('one','two')
>>> t2=t1[:]
>>> t1 is t2
True
>>> t3=deepcopy(t1)
>>> t3 is t2 is t1
True

また、(整数のような) 不変オブジェクトの変更可能なリストには、リスト メンバーを挿入することができます。

>>> li1=[1,2,3]
>>> li2=deepcopy(li1)
>>> li2 == li1
True
>>> li2 is li1
False
>>> li1[0] is li2[0]
True

そのため、何かをコピーすることがわかっている python 操作を使用できますが、最終結果はインターンされた不変オブジェクトへの別の参照になります。このisテストは、アイテムが変更可能である場合に作成されるコピーの決定的なテストにすぎません。

于 2013-09-28T18:31:20.897 に答える