リストをディープコピーする方法は?


150

リストのコピーに問題があります:

E0から取得した後'get_edge'、をE0呼び出してのコピーを作成します'E0_copy = list(E0)'。ここで私は推測するE0_copyのディープコピーであるE0、と私は合格E0_copy'karger(E)'。しかし、メイン関数で。forループ
'print E0[1:10]'前の結果がforループの後の結果と同じではないのはなぜですか?

以下は私のコードです:

def get_graph():
    f=open('kargerMinCut.txt')
    G={}
    for line in f:
        ints = [int(x) for x in line.split()]
        G[ints[0]]=ints[1:len(ints)]
    return G

def get_edge(G):
    E=[]
    for i in range(1,201):
        for v in G[i]:
            if v>i:
                E.append([i,v])
    print id(E)
    return E

def karger(E):
    import random
    count=200 
    while 1:
        if count == 2:
            break
        edge = random.randint(0,len(E)-1)
        v0=E[edge][0]
        v1=E[edge][1]                   
        E.pop(edge)
        if v0 != v1:
            count -= 1
            i=0
            while 1:
                if i == len(E):
                    break
                if E[i][0] == v1:
                    E[i][0] = v0
                if E[i][1] == v1:
                    E[i][1] = v0
                if E[i][0] == E[i][1]:
                    E.pop(i)
                    i-=1
                i+=1

    mincut=len(E)
    return mincut


if __name__=="__main__":
    import copy
    G = get_graph()
    results=[]
    E0 = get_edge(G)
    print E0[1:10]               ## this result is not equal to print2
    for k in range(1,5):
        E0_copy=list(E0)         ## I guess here E0_coypy is a deep copy of E0
        results.append(karger(E0_copy))
       #print "the result is %d" %min(results)
    print E0[1:10]               ## this is print2

2
また、b = a [:]は浅いコピーです。stackoverflow.com/questions/16270374/…を
Arvind Haran

回答:


230

E0_copyディープコピーではありません。あなたは使用して深いコピーを作成していないlist()(どちらlist(...)testList[:]浅いコピーです)。

copy.deepcopy(...)リストのディープコピーに使用します。

deepcopy(x, memo=None, _nil=[])
    Deep copy operation on arbitrary Python objects.

次のスニペットを参照してください-

>>> a = [[1, 2, 3], [4, 5, 6]]
>>> b = list(a)
>>> a
[[1, 2, 3], [4, 5, 6]]
>>> b
[[1, 2, 3], [4, 5, 6]]
>>> a[0][1] = 10
>>> a
[[1, 10, 3], [4, 5, 6]]
>>> b   # b changes too -> Not a deepcopy.
[[1, 10, 3], [4, 5, 6]]

今、deepcopy操作を見てください

>>> import copy
>>> b = copy.deepcopy(a)
>>> a
[[1, 10, 3], [4, 5, 6]]
>>> b
[[1, 10, 3], [4, 5, 6]]
>>> a[0][1] = 9
>>> a
[[1, 9, 3], [4, 5, 6]]
>>> b    # b doesn't change -> Deep Copy
[[1, 10, 3], [4, 5, 6]]

3
Thanks.But id(E0)はid(E0_copy)と等しくないので、list()はディープコピーだと思いました。なぜそれが起こるのか説明できますか?
シェン

15
list(...)は、内部オブジェクトのコピーを再帰的に作成しません。前の変数から内部リストを参照しながら、最も外側のリストのコピーのみを作成するため、内部リストを変更すると、変更は元のリストと浅いコピーの両方に反映されます。
Sukrit Kalra 2013

1
浅いコピーがid(a [0])== id(b [0])をチェックすることで内部リストを参照していることがわかります。ここで、b = list(a)とaはリストのリストです。
スクリットカラ2013

list1.append(list2)は、
list2の

60

多くのプログラマーは、リンクリストをディープコピーするように求められる1つまたは2つのインタビューの問題に遭遇したと思いますが、この問題は思ったより難しいです!

Pythonでは、2つの便利な機能を持つ「コピー」と呼ばれるモジュールがあります

import copy
copy.copy()
copy.deepcopy()

copy()は浅いコピー関数であり、与えられた引数が複合データ構造、たとえばリストである場合、Pythonは同じタイプの別のオブジェクト(この場合は新しいリスト)を作成しますが、古いリスト内のすべてのものに対して、参照のみがコピーされます

# think of it like
newList = [elem for elem in oldlist]

直感的に、deepcopy()は同じパラダイムに従うと想定できます。唯一の違いは、各要素に対して、(mbcoderの答えのように)再帰的にdeepcopyを呼び出すことです

しかし、これは間違っています!

deepcopy()は、実際には元の複合データのグラフィック構造を保持します。

a = [1,2]
b = [a,a] # there's only 1 object a
c = deepcopy(b)

# check the result
c[0] is a # return False, a new object a' is created
c[0] is c[1] # return True, c is [a',a'] not [a',a'']

これはトリッキーな部分です。deepcopy()の処理中に、ハッシュテーブル(Pythonの辞書)を使用して、「old_object refからnew_object refへ」をマッピングします。これにより、不要な重複が防止され、コピーされた複合データの構造が保持されます。

公式ドキュメント


18

リストの内容がプリミティブデータタイプの場合、内包表記を使用できます。

new_list = [i for i in old_list]

次のような多次元リストにネストできます。

new_grid = [[i for i in row] for row in grid]

5

あなたlist elementsがしている場合はimmutable objects、これを使用できます。そうでない場合はdeepcopycopyモジュールから使用する必要があります。

listこのようなディープコピーには最短の方法を使用することもできます。

a = [0,1,2,3,4,5,6,7,8,9,10]
b = a[:] #deep copying the list a and assigning it to b
print id(a)
20983280
print id(b)
12967208

a[2] = 20
print a
[0, 1, 20, 3, 4, 5, 6, 7, 8, 9,10]
print b
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10]

21
これはディープコピーではありません。
スクリットカラ2013

1
それは何ですか。同じ値の2つの異なる辞書(それぞれのIDを確認できます)があります。
tailor_raj 2013

これを読んで、[:]は浅いコピーを作成するだけで、その中にオブジェクトのコピーを再帰的に作成することはありません。
スクリットカラ2013

1
ありがとう。これを使用すると、新しいリストが作成されますが、新しいリストのすべての要素はコピーのみであり、前のオブジェクトと同じオブジェクト(同じID)を持っていますか?
tailor_raj

ネストされたリストを使用してみてください。リストのネストされたアイテムを更新します。リストbでも更新されます。これは、a [:]がディープコピーではないことを意味します。
AnupamChugh

2

再帰的なディープコピー機能だけです。

def deepcopy(A):
    rt = []
    for elem in A:
        if isinstance(elem,list):
            rt.append(deepcopy(elem))
        else:
            rt.append(elem)
    return rt

編集:Cfreakが述べたように、これはすでにcopyモジュールに実装されています。


4
モジュールの標準deepcopy()関数を再実装する理由はありませんcopy
Cfreak

1

リストをツリーと見なすと、Pythonのdeep_copyは次のように最もコンパクトに記述できます。

def deep_copy(x):
    if not isinstance(x, list): return x
    else: return map(deep_copy, x)


0

これはもっとpythonicです

my_list = [0, 1, 2, 3, 4, 5]  # some list
my_list_copy = list(my_list)  # my_list_copy and my_list does not share reference now.

注:これは、参照されるオブジェクトのリストでは安全ではありません


2
これは動作しません。私はそれがかもしれないと思ったがちょうどチェックした。良い例として辞書のリストを試してください
Shashank Singh

@ShashankSinghはい、エントリは参照タグ(メモリの場所を指す)であるため、これは辞書のリストでは機能しません。したがって、このメソッドを使用して辞書のリストを複製すると、新しいリストが作成されますが、エントリは辞書であるため、同じメモリ位置を参照します。
Kwaw Annor
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.