割り当て後にリストが予期せず変更されます。これを防ぐために、クローンまたはコピーするにはどうすればよいですか?

2712
aF. 2010-04-10 22:49.

を使用している間new_list = my_listnew_list変更はmy_list毎回変更されます。これはなぜですか。リストを複製またはコピーして、それを防ぐにはどうすればよいですか。

19 answers

3501
Felix Kling 2010-04-10 22:55.

を使用するとnew_list = my_list、実際には2つのリストはありません。割り当ては、実際のリストではなくリストへの参照をコピーするだけなので、両方とも割り当て後に同じリストnew_listmy_list参照します。

リストを実際にコピーするには、さまざまな可能性があります。

  • 組み込みのlist.copy()メソッド(Python 3.3以降で使用可能)を使用できます。

    new_list = old_list.copy()
    
  • あなたはそれをスライスすることができます:

    new_list = old_list[:]
    

    これに関するAlexMartelliの意見(少なくとも2007年に遡る)は、これは奇妙な構文であり、これまで使用しても意味がないというものです。;)(彼の意見では、次のものはより読みやすいです)。

  • 組み込みlist()関数を使用できます。

    new_list = list(old_list)
    
  • ジェネリックを使用できますcopy.copy()

    import copy
    new_list = copy.copy(old_list)
    

    これは、最初list()のデータ型を見つける必要があるためよりも少し遅くなりますold_list

  • リストにオブジェクトが含まれていて、それらもコピーする場合は、generic copy.deepcopy():を使用します。

    import copy
    new_list = copy.deepcopy(old_list)
    

    明らかに、最も遅く、最もメモリを必要とする方法ですが、避けられない場合もあります。

例:

import copy

class Foo(object):
    def __init__(self, val):
         self.val = val

    def __repr__(self):
        return 'Foo({!r})'.format(self.val)

foo = Foo(1)

a = ['foo', foo]
b = a.copy()
c = a[:]
d = list(a)
e = copy.copy(a)
f = copy.deepcopy(a)

# edit orignal list and instance 
a.append('baz')
foo.val = 5

print('original: %r\nlist.copy(): %r\nslice: %r\nlist(): %r\ncopy: %r\ndeepcopy: %r'
      % (a, b, c, d, e, f))

結果:

original: ['foo', Foo(5), 'baz']
list.copy(): ['foo', Foo(5)]
slice: ['foo', Foo(5)]
list(): ['foo', Foo(5)]
copy: ['foo', Foo(5)]
deepcopy: ['foo', Foo(1)]
644
cryo 2010-04-11 00:16.

フェリックスはすでに優れた答えを提供しましたが、私はさまざまな方法の速度比較を行うと思いました:

  1. 10.59秒(105.9us / itn)- copy.deepcopy(old_list)
  2. 10.16秒(101.6us / itn)-deepcopyをCopy()使用してクラスをコピーする純粋なPythonメソッド
  3. 1.488秒(14.88us / itn)-Copy()クラスをコピーしない純粋なPythonメソッド(dict /リスト/タプルのみ)
  4. 0.325秒(3.25us / itn)- for item in old_list: new_list.append(item)
  5. 0.217秒(2.17us / itn)- [i for i in old_list](リスト内包表記)
  6. 0.186秒(1.86us / itn)- copy.copy(old_list)
  7. 0.075秒(0.75us / itn)- list(old_list)
  8. 0.053秒(0.53us / itn)- new_list = []; new_list.extend(old_list)
  9. 0.039秒(0.39us / itn)- old_list[:](リストスライス)

したがって、最速はリストスライスです。ただしcopy.copy()list[:]list(list)は異なりcopy.deepcopy()、Pythonバージョンではリスト、辞書、クラスインスタンスがリストにコピーされないため、元のリストが変更されると、コピーされたリストでも変更されることに注意してください。その逆も同様です。

(誰かが興味を持っているか、問題を提起したい場合のスクリプトは次のとおりです:)

from copy import deepcopy

class old_class:
    def __init__(self):
        self.blah = 'blah'

class new_class(object):
    def __init__(self):
        self.blah = 'blah'

dignore = {str: None, unicode: None, int: None, type(None): None}

def Copy(obj, use_deepcopy=True):
    t = type(obj)

    if t in (list, tuple):
        if t == tuple:
            # Convert to a list if a tuple to 
            # allow assigning to when copying
            is_tuple = True
            obj = list(obj)
        else: 
            # Otherwise just do a quick slice copy
            obj = obj[:]
            is_tuple = False

        # Copy each item recursively
        for x in xrange(len(obj)):
            if type(obj[x]) in dignore:
                continue
            obj[x] = Copy(obj[x], use_deepcopy)

        if is_tuple: 
            # Convert back into a tuple again
            obj = tuple(obj)

    elif t == dict: 
        # Use the fast shallow dict copy() method and copy any 
        # values which aren't immutable (like lists, dicts etc)
        obj = obj.copy()
        for k in obj:
            if type(obj[k]) in dignore:
                continue
            obj[k] = Copy(obj[k], use_deepcopy)

    elif t in dignore: 
        # Numeric or string/unicode? 
        # It's immutable, so ignore it!
        pass 

    elif use_deepcopy: 
        obj = deepcopy(obj)
    return obj

if __name__ == '__main__':
    import copy
    from time import time

    num_times = 100000
    L = [None, 'blah', 1, 543.4532, 
         ['foo'], ('bar',), {'blah': 'blah'},
         old_class(), new_class()]

    t = time()
    for i in xrange(num_times):
        Copy(L)
    print 'Custom Copy:', time()-t

    t = time()
    for i in xrange(num_times):
        Copy(L, use_deepcopy=False)
    print 'Custom Copy Only Copying Lists/Tuples/Dicts (no classes):', time()-t

    t = time()
    for i in xrange(num_times):
        copy.copy(L)
    print 'copy.copy:', time()-t

    t = time()
    for i in xrange(num_times):
        copy.deepcopy(L)
    print 'copy.deepcopy:', time()-t

    t = time()
    for i in xrange(num_times):
        L[:]
    print 'list slicing [:]:', time()-t

    t = time()
    for i in xrange(num_times):
        list(L)
    print 'list(L):', time()-t

    t = time()
    for i in xrange(num_times):
        [i for i in L]
    print 'list expression(L):', time()-t

    t = time()
    for i in xrange(num_times):
        a = []
        a.extend(L)
    print 'list extend:', time()-t

    t = time()
    for i in xrange(num_times):
        a = []
        for y in L:
            a.append(y)
    print 'list append:', time()-t

    t = time()
    for i in xrange(num_times):
        a = []
        a.extend(i for i in L)
    print 'generator expression extend:', time()-t
160
anatoly techtonik 2013-07-24 02:32.

Python 3.3以降ではメソッドが追加されると言われていますが、これはスライスと同じくらい高速である必要があります。list.copy()

newlist = old_list.copy()

133
Aaron Hall 2014-10-26 02:13.

Pythonでリストを複製またはコピーするためのオプションは何ですか?

Python 3では、次の方法で浅いコピーを作成できます。

a_copy = a_list.copy()

Python 2および3では、オリジナルの完全なスライスを含む浅いコピーを取得できます。

a_copy = a_list[:]

説明

リストをコピーするには、2つのセマンティックな方法があります。浅いコピーは同じオブジェクトの新しいリストを作成し、深いコピーは新しい同等のオブジェクトを含む新しいリストを作成します。

浅いリストのコピー

浅いコピーは、リスト自体のみをコピーします。これは、リスト内のオブジェクトへの参照のコンテナーです。含まれているオブジェクトが変更可能で、1つが変更された場合、変更は両方のリストに反映されます。

Python 2と3でこれを行うには、さまざまな方法があります。Python2の方法は、Python3でも機能します。

Python 2

Python 2では、リストの浅いコピーを作成する慣用的な方法は、元の完全なスライスを使用することです。

a_copy = a_list[:]

リストコンストラクターにリストを渡すことによっても同じことを実現できます。

a_copy = list(a_list)

ただし、コンストラクターの使用は効率が低くなります。

>>> timeit
>>> l = range(20)
>>> min(timeit.repeat(lambda: l[:]))
0.30504298210144043
>>> min(timeit.repeat(lambda: list(l)))
0.40698814392089844

Python 3

Python 3では、リストは次のlist.copyメソッドを取得します。

a_copy = a_list.copy()

Python 3.5の場合:

>>> import timeit
>>> l = list(range(20))
>>> min(timeit.repeat(lambda: l[:]))
0.38448613602668047
>>> min(timeit.repeat(lambda: list(l)))
0.6309100328944623
>>> min(timeit.repeat(lambda: l.copy()))
0.38122922903858125

別のポインタを作成してもコピー作成されませ

new_list = my_listを使用すると、my_listが変更されるたびにnew_listが変更されます。どうしてこれなの?

my_listは、メモリ内の実際のリストを指す単なる名前です。new_list = my_listコピーを作成していないと言うときは、メモリ内の元のリストを指す別の名前を追加しているだけです。リストのコピーを作成するときにも、同様の問題が発生する可能性があります。

>>> l = [[], [], []]
>>> l_copy = l[:]
>>> l_copy
[[], [], []]
>>> l_copy[0].append('foo')
>>> l_copy
[['foo'], [], []]
>>> l
[['foo'], [], []]

リストはコンテンツへのポインタの配列にすぎないため、浅いコピーはポインタをコピーするだけであり、2つの異なるリストがありますが、コンテンツは同じです。コンテンツのコピーを作成するには、ディープコピーが必要です。

ディープコピー

リストのディープコピーdeepcopycopyを作成するには、Python 2または3で、次のモジュールを使用します。

import copy
a_deep_copy = copy.deepcopy(a_list)

これにより、新しいサブリストを作成する方法を示すには、次のようにします。

>>> import copy
>>> l
[['foo'], [], []]
>>> l_deep_copy = copy.deepcopy(l)
>>> l_deep_copy[0].pop()
'foo'
>>> l_deep_copy
[[], [], []]
>>> l
[['foo'], [], []]

したがって、深くコピーされたリストは、元のリストとはまったく異なるリストであることがわかります。独自の関数をロールすることはできますが、ロールしないでください。標準ライブラリのディープコピー機能を使用すると、他の方法では発生しないバグが発生する可能性があります。

使用しないでください eval

これはディープコピーの方法として使用されているように見えるかもしれませんが、実行しないでください。

problematic_deep_copy = eval(repr(a_list))
  1. 特に、信頼できないソースから何かを評価している場合は危険です。
  2. コピーしているサブ要素に、同等の要素を再現するために評価できる表現がない場合、それは信頼できません。
  3. また、パフォーマンスも低下します。

64ビットPython2.7の場合:

>>> import timeit
>>> import copy
>>> l = range(10)
>>> min(timeit.repeat(lambda: copy.deepcopy(l)))
27.55826997756958
>>> min(timeit.repeat(lambda: eval(repr(l))))
29.04534101486206

64ビットPython3.5の場合:

>>> import timeit
>>> import copy
>>> l = list(range(10))
>>> min(timeit.repeat(lambda: copy.deepcopy(l)))
16.84255409205798
>>> min(timeit.repeat(lambda: eval(repr(l))))
34.813894678023644
57
jack 2014-11-24 06:45.

適切なコピーを作成する方法を説明する回答はすでにたくさんありますが、元の「コピー」が失敗した理由を説明しているものはありません。

Pythonは変数に値を格納しません。名前をオブジェクトにバインドします。元の割り当ては、によって参照されるオブジェクトを取得し、my_listそれをバインドしnew_listました。どの名前を使用しても、リストは1つしかないため、それを参照するときに行われた変更は、を参照my_listするときにも保持されnew_listます。この質問に対する他の各回答は、にバインドする新しいオブジェクトを作成するさまざまな方法を提供しますnew_list

リストの各要素は、各要素がオブジェクトに非排他的にバインドされるという点で、名前のように機能します。浅いコピーは、要素が以前と同じオブジェクトにバインドする新しいリストを作成します。

new_list = list(my_list)  # or my_list[:], but I prefer this syntax
# is simply a shorter way of:
new_list = [element for element in my_list]

リストのコピーをさらに一歩進めるには、リストが参照する各オブジェクトをコピーし、それらの要素のコピーを新しいリストにバインドします。

import copy  
# each element must have __copy__ defined for this...
new_list = [copy.copy(element) for element in my_list]

リストがその要素にバインドされているのと同じように、リストの各要素が他のオブジェクトを参照している可能性があるため、これはまだディープコピーではありません。リスト内のすべての要素を再帰的にコピーしてから、各要素によって参照される他のオブジェクトなどを再帰的にコピーするには、次のようにします。ディープコピーを実行します。

import copy
# each element must have __deepcopy__ defined for this...
new_list = copy.deepcopy(my_list)

コピーのコーナーケースの詳細については、ドキュメントを参照してください。

46
Aaditya Ura 2017-11-13 21:04.

最初から始めて、この質問を調べてみましょう。

したがって、2つのリストがあるとします。

list_1=['01','98']
list_2=[['01','98']]

そして、最初のリストから始めて、両方のリストをコピーする必要があります。

それでは、最初に変数copyを元のリストに設定してみましょうlist_1

copy=list_1

今、あなたがコピーがlist_1をコピーしたと思っているなら、あなたは間違っています。このid関数は、2つの変数が同じオブジェクトを指すことができるかどうかを示します。これを試してみましょう:

print(id(copy))
print(id(list_1))

出力は次のとおりです。

4329485320
4329485320

両方の変数はまったく同じ引数です。驚きましたか?

つまり、Pythonは変数に何も格納しないことがわかっているので、変数はオブジェクトを参照しているだけで、オブジェクトは値を格納します。ここでオブジェクトはですが、list2つの異なる変数名で同じオブジェクトへの2つの参照を作成しました。これは、両方の変数が異なる名前で同じオブジェクトを指していることを意味します。

あなたがするときcopy=list_1、それは実際にやっています:

この画像では、list_1とcopyに2つの変数名がありますが、オブジェクトは両方の変数で同じです。 list

したがって、コピーされたリストを変更しようとすると、元のリストも変更されます。これは、リストが1つしかないため、コピーされたリストまたは元のリストのどちらから行っても、そのリストを変更するためです。

copy[0]="modify"

print(copy)
print(list_1)

出力:

['modify', '98']
['modify', '98']

したがって、元のリストを変更しました:

それでは、リストをコピーするためのpythonicメソッドに移りましょう。

copy_1=list_1[:]

この方法で、最初に発生した問題が修正されます。

print(id(copy_1))
print(id(list_1))

4338792136
4338791432

したがって、両方のリストのIDが異なることがわかります。これは、両方の変数が異なるオブジェクトを指していることを意味します。つまり、ここで実際に起こっていることは次のとおりです。

次に、リストを変更して、前の問題がまだ発生しているかどうかを確認しましょう。

copy_1[0]="modify"

print(list_1)
print(copy_1)

出力は次のとおりです。

['01', '98']
['modify', '98']

ご覧のとおり、コピーされたリストのみが変更されました。それはそれがうまくいったことを意味します。

終わったと思いますか?いいえ。ネストされたリストをコピーしてみましょう。

copy_2=list_2[:]

list_2のコピーである別のオブジェクトを参照する必要がありますlist_2。確認しよう:

print(id((list_2)),id(copy_2))

出力を取得します。

4330403592 4330403528

これで、両方のリストが異なるオブジェクトを指していると想定できるので、それを変更して、必要なものが提供されていることを確認しましょう。

copy_2[0][1]="modify"

print(list_2,copy_2)

これにより、次の出力が得られます。

[['01', 'modify']] [['01', 'modify']]

以前に使用したのと同じ方法が機能したため、これは少し混乱しているように見えるかもしれません。これを理解してみましょう。

あなたがするとき:

copy_2=list_2[:]

内側のリストではなく、外側のリストのみをコピーしています。idこの関数をもう一度使用して、これを確認できます。

print(id(copy_2[0]))
print(id(list_2[0]))

出力は次のとおりです。

4329485832
4329485832

私たちがそうするときcopy_2=list_2[:]、これは起こります:

リストのコピーを作成しますが、ネストされたリストのコピーではなく、外側のリストのコピーのみを作成します。ネストされたリストは両方の変数で同じです。したがって、ネストされたリストを変更しようとすると、ネストされたリストオブジェクトが同じであるため、元のリストも変更されます。両方のリストについて。

解決策は何ですか?解決策はdeepcopy関数です。

from copy import deepcopy
deep=deepcopy(list_2)

これを確認しましょう:

print(id((list_2)),id(deep))

4322146056 4322148040

両方の外側のリストは異なるIDを持っているので、内側のネストされたリストでこれを試してみましょう。

print(id(deep[0]))
print(id(list_2[0]))

出力は次のとおりです。

4322145992
4322145800

ご覧のとおり、両方のIDが異なります。つまり、ネストされた両方のリストが異なるオブジェクトを指していると想定できます。

これは、deep=deepcopy(list_2)実際に何が起こるかを実行するときを意味します。

両方のネストされたリストは異なるオブジェクトを指しており、ネストされたリストの個別のコピーがあります。

次に、ネストされたリストを変更して、前の問題が解決したかどうかを確認してみましょう。

deep[0][1]="modify"
print(list_2,deep)

出力:

[['01', '98']] [['01', 'modify']]

ご覧のとおり、元のネストされたリストは変更されず、コピーされたリストのみが変更されました。

38
Paul Tarjan 2010-04-10 22:53.

使用する thing[:]

>>> a = [1,2]
>>> b = a[:]
>>> a += [3]
>>> a
[1, 2, 3]
>>> b
[1, 2]
>>> 
37
River 2017-04-05 15:01.

Python3.6のタイミング

Python3.6.8を使用したタイミングの結果は次のとおりです。これらの時間は絶対的なものではなく、相互に関連していることに注意してください。

私は浅いコピーだけを行うことに固執し、Python2では不可能だったいくつかの新しいメソッドlist.copy()((Python3スライスと同等)や2つの形式のリストアンパック(*new_list, = listおよびnew_list = [*list])など)も追加しました。

METHOD                  TIME TAKEN
b = [*a]                2.75180600000021
b = a * 1               3.50215399999990
b = a[:]                3.78278899999986  # Python2 winner (see above)
b = a.copy()            4.20556500000020  # Python3 "slice equivalent" (see above)
b = []; b.extend(a)     4.68069800000012
b = a[0:len(a)]         6.84498999999959
*b, = a                 7.54031799999984
b = list(a)             7.75815899999997
b = [i for i in a]      18.4886440000000
b = copy.copy(a)        18.8254879999999
b = []
for item in a:
  b.append(item)        35.4729199999997

Python2の勝者は依然としてうまく機能していることがわかりますがlist.copy()、特に後者の優れた読みやすさを考えると、Python3をそれほど上回っていません。

ダークホースは、開梱と再梱包の方法(b = [*a])であり、生のスライスよりも約25%速く、他の開梱方法(*b, = a)の2倍以上高速です。

b = a * 1 また、驚くほどうまくいきます。

これらのメソッドは、リスト以外の入力に対して同等の結果を出力しないことに注意してください。それらはすべてスライス可能なオブジェクトに対して機能し、いくつかは反復可能copy.copy()なオブジェクトに対して機能しますが、より一般的なPythonオブジェクトに対してのみ機能します。


利害関係者向けのテストコードは次のとおりです(ここからのテンプレート):

import timeit

COUNT = 50000000
print("Array duplicating. Tests run", COUNT, "times")
setup = 'a = [0,1,2,3,4,5,6,7,8,9]; import copy'

print("b = list(a)\t\t", timeit.timeit(stmt='b = list(a)', setup=setup, number=COUNT))
print("b = copy.copy(a)\t", timeit.timeit(stmt='b = copy.copy(a)', setup=setup, number=COUNT))
print("b = a.copy()\t\t", timeit.timeit(stmt='b = a.copy()', setup=setup, number=COUNT))
print("b = a[:]\t\t", timeit.timeit(stmt='b = a[:]', setup=setup, number=COUNT))
print("b = a[0:len(a)]\t\t", timeit.timeit(stmt='b = a[0:len(a)]', setup=setup, number=COUNT))
print("*b, = a\t\t\t", timeit.timeit(stmt='*b, = a', setup=setup, number=COUNT))
print("b = []; b.extend(a)\t", timeit.timeit(stmt='b = []; b.extend(a)', setup=setup, number=COUNT))
print("b = []; for item in a: b.append(item)\t", timeit.timeit(stmt='b = []\nfor item in a:  b.append(item)', setup=setup, number=COUNT))
print("b = [i for i in a]\t", timeit.timeit(stmt='b = [i for i in a]', setup=setup, number=COUNT))
print("b = [*a]\t\t", timeit.timeit(stmt='b = [*a]', setup=setup, number=COUNT))
print("b = a * 1\t\t", timeit.timeit(stmt='b = a * 1', setup=setup, number=COUNT))
34
erisco 2010-04-10 22:53.

これを行うためのPythonのイディオムは newList = oldList[:]

20
AMR 2015-07-10 17:51.

他のすべての寄稿者は素晴らしい答えを出しました。これは単一のディメンション(平準化された)リストがある場合に機能しますが、これまでに説明した方法の中でcopy.deepcopy()、リストの複製/コピーにのみ機能し、ネストされたlistオブジェクトを指すようにはなりません。多次元のネストされたリスト(リストのリスト)の操作。一方でフェリックスクリングが彼の答えでそれを指し、もう少し問題と可能性へのより高速な代替を証明するかもしれないビルトインを使用して回避策がありますdeepcopy

一方でnew_list = old_list[:]copy.copy(old_list)'とPy3kのためのold_list.copy()単一の平準リストの仕事、彼らが指し示すに戻りlist内にネストされたオブジェクトold_listnew_list、との1への変更listオブジェクトは、他に生まれついています。

編集:新しい情報が明らかになりました

アーロンホールとPM2Ringの 両方が指摘したように、使用eval()することは悪い考えであるだけでなく、よりもはるかに遅いですcopy.deepcopy()

これは、多次元リストの場合、唯一のオプションがcopy.deepcopy()。であることを意味します。そうは言っても、適度なサイズの多次元配列で使用しようとするとパフォーマンスが大幅に低下するため、これは実際にはオプションではありません。私はtimeit42x42の配列を使用しようとしましたが、バイオインフォマティクスアプリケーションでは前代未聞ではなく、それほど大きくもありませんでした。応答を待つのをあきらめて、この投稿に編集内容を入力し始めました。

その場合、唯一の現実的な選択肢は、複数のリストを初期化し、それらを個別に処理することであるように思われます。多次元リストのコピーを処理する方法について、他に何か提案があれば、よろしくお願いします。

他の人が述べているように、モジュールと多次元リストの使用には重大なパフォーマンスの問題がありますcopycopy.deepcopy

14
SCB 2018-02-26 16:33.

これがまだ言及されていないことに驚いたので、完全を期すために...

「スプラット演算子」を使用してリストの解凍を実行でき*ます。これにより、リストの要素もコピーされます。

old_list = [1, 2, 3]

new_list = [*old_list]

new_list.append(4)
old_list == [1, 2, 3]
new_list == [1, 2, 3, 4]

この方法の明らかな欠点は、Python3.5以降でのみ使用できることです。

ただし、タイミング的には、これは他の一般的な方法よりもパフォーマンスが優れているようです。

x = [random.random() for _ in range(1000)]

%timeit a = list(x)
%timeit a = x.copy()
%timeit a = x[:]

%timeit a = [*x]

#: 2.47 µs ± 38.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
#: 2.47 µs ± 54.6 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
#: 2.39 µs ± 58.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

#: 2.22 µs ± 43.2 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
8
jainashish 2017-11-01 22:08.

Pythonのバージョンに依存しない非常に単純なアプローチは、ほとんどの場合に使用できる(少なくとも私はそうする)すでに与えられた回答にはありませんでした:

new_list = my_list * 1       #Solution 1 when you are not using nested lists

ただし、my_listに他のコンテナ(ネストされたリストなど)が含まれている場合は、コピーライブラリから上記の回答で提案されているようにdeepcopyを使用する必要があります。例えば:

import copy
new_list = copy.deepcopy(my_list)   #Solution 2 when you are using nested lists

ボーナス:要素をコピーしたくない場合は、以下を使用してください(別名シャローコピー):

new_list = my_list[:]

ソリューション#1とソリューション#2の違いを理解しましょう

>>> a = range(5)
>>> b = a*1
>>> a,b
([0, 1, 2, 3, 4], [0, 1, 2, 3, 4])
>>> a[2] = 55 
>>> a,b
([0, 1, 55, 3, 4], [0, 1, 2, 3, 4])

ご覧のとおり、ネストされたリストを使用していない場合、ソリューション#1は完全に機能しました。ネストされたリストにソリューション#1を適用するとどうなるかを確認しましょう。

>>> from copy import deepcopy
>>> a = [range(i,i+4) for i in range(3)]
>>> a
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]
>>> b = a*1
>>> c = deepcopy(a)
>>> for i in (a, b, c): print i   
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]
>>> a[2].append('99')
>>> for i in (a, b, c): print i   
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5, 99]]
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5, 99]]   #Solution#1 didn't work in nested list
[[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]       #Solution #2 - DeepCopy worked in nested list
8
Chris_Rands 2018-05-17 04:31.

独自のカスタムクラスを定義し、属性を保持したい場合は、たとえばPython 3のように、代替手段ではなく、copy.copy()またはを使用する必要がある場合があることに注意してくださいcopy.deepcopy()

import copy

class MyList(list):
    pass

lst = MyList([1,2,3])

lst.name = 'custom list'

d = {
'original': lst,
'slicecopy' : lst[:],
'lstcopy' : lst.copy(),
'copycopy': copy.copy(lst),
'deepcopy': copy.deepcopy(lst)
}


for k,v in d.items():
    print('lst: {}'.format(k), end=', ')
    try:
        name = v.name
    except AttributeError:
        name = 'NA'
    print('name: {}'.format(name))

出力:

lst: original, name: custom list
lst: slicecopy, name: NA
lst: lstcopy, name: NA
lst: copycopy, name: custom list
lst: deepcopy, name: custom list
5
Ravi Shankar 2017-06-27 11:03.
new_list = my_list[:]

new_list = my_listこれを理解してみてください。my_listが場所Xのヒープメモリにある、つまりmy_listがXを指しているとしましょう。これを割り当てるnew_list = my_listことにより、new_listがXを指すようになります。これはシャローコピーと呼ばれます。

ここで、割り当てた場合new_list = my_list[:]、my_listの各オブジェクトをnew_listにコピーするだけです。これはディープコピーとして知られています。

これを行うことができる他の方法は次のとおりです。

  • new_list = list(old_list)
  • import copy new_list = copy.deepcopy(old_list)
3
Corman 2019-09-08 16:25.

他のいくつかの回答とは少し違うものを投稿したかったのです。これはおそらく最も理解しやすい、または最速のオプションではありませんが、ディープコピーがどのように機能するかについての内部ビューを少し提供し、ディープコピーの別の代替オプションでもあります。私の関数にバグがあるかどうかは問題ではありません。これは、質問の回答のようなオブジェクトをコピーする方法を示すだけでなく、ディープコピーがコアでどのように機能するかを説明するためのポイントとして使用するためです。

ディープコピー機能の中核となるのは、シャローコピーを作成する方法です。どうやって?シンプル。ディープコピー関数は、不変オブジェクトのコンテナのみを複製します。ネストされたリストをディープコピーすると、外側のリストのみが複製され、リスト内の可変オブジェクトは複製されません。コンテナを複製しているだけです。同じことがクラスでも機能します。クラスをディープコピーすると、その可変属性がすべてディープコピーされます。それで、どうやって?リスト、dict、タプル、iter、クラス、クラスインスタンスなどのコンテナをコピーするだけでよいのはなぜですか?

それは簡単です。可変オブジェクトは実際には複製できません。変更することはできないため、単一の値にすぎません。つまり、文字列、数字、bool、またはそれらのいずれかを複製する必要はありません。しかし、どのようにコンテナを複製しますか?シンプル。すべての値で新しいコンテナを初期化するだけです。ディープコピーは再帰に依存しています。コンテナがなくなるまで、コンテナが内部にあるものも含めて、すべてのコンテナを複製します。コンテナは不変のオブジェクトです。

それがわかれば、参照なしでオブジェクトを完全に複製するのは非常に簡単です。これは、基本的なデータ型をディープコピーするための関数です(カスタムクラスでは機能しませんが、いつでも追加できます)

def deepcopy(x):
  immutables = (str, int, bool, float)
  mutables = (list, dict, tuple)
  if isinstance(x, immutables):
    return x
  elif isinstance(x, mutables):
    if isinstance(x, tuple):
      return tuple(deepcopy(list(x)))
    elif isinstance(x, list):
      return [deepcopy(y) for y in x]
    elif isinstance(x, dict):
      values = [deepcopy(y) for y in list(x.values())]
      keys = list(x.keys())
      return dict(zip(keys, values))

Python独自の組み込みディープコピーは、その例に基づいています。唯一の違いは、他のタイプをサポートし、属性を新しい複製クラスに複製することでユーザークラスをサポートし、メモリストまたは辞書を使用して既に表示されているオブジェクトへの参照で無限再帰をブロックすることです。そして、それは本当に深いコピーを作成するためのそれです。基本的に、深いコピーを作成することは、浅いコピーを作成することです。この答えが質問に何かを追加することを願っています。

あなたがこのリストを持っているとしましょう:[1、2、3]。不変の数は複製できませんが、他のレイヤーは複製できます。リスト内包表記を使用して複製できます:[x for x in [1、2、3]

ここで、次のリストがあると想像してください:[[1、2]、[3、4]、[5、6]]。今回は、再帰を使用してリストのすべてのレイヤーをディープコピーする関数を作成します。前のリスト内包表記の代わりに:

[x for x in _list]

リストには新しいものを使用します。

[deepcopy_list(x) for x in _list]

そしてdeepcopy_listは次のようになります:

def deepcopy_list(x):
  if isinstance(x, (str, bool, float, int)):
    return x
  else:
    return [deepcopy_list(y) for y in x]

これで、str、bool、floast、intのリスト、さらにはリストを再帰を使用して無限に多くのレイヤーにディープコピーできる関数ができました。そして、あなたはそれを持っています、ディープコピー。

TLDR:Deepcopyは再帰を使用してオブジェクトを複製し、不変オブジェクトは複製できないため、以前と同じ不変オブジェクトを返すだけです。ただし、オブジェクトの最も外側の可変レイヤーに到達するまで、可変オブジェクトの最も内側のレイヤーをディープコピーします。

3
B.Mr.W. 2019-11-24 09:01.

idとgcを介してメモリを調べるための少し実用的な視点。

>>> b = a = ['hell', 'word']
>>> c = ['hell', 'word']

>>> id(a), id(b), id(c)
(4424020872, 4424020872, 4423979272) 
     |           |
      -----------

>>> id(a[0]), id(b[0]), id(c[0])
(4424018328, 4424018328, 4424018328) # all referring to same 'hell'
     |           |           |
      -----------------------

>>> id(a[0][0]), id(b[0][0]), id(c[0][0])
(4422785208, 4422785208, 4422785208) # all referring to same 'h'
     |           |           |
      -----------------------

>>> a[0] += 'o'
>>> a,b,c
(['hello', 'word'], ['hello', 'word'], ['hell', 'word'])  # b changed too
>>> id(a[0]), id(b[0]), id(c[0])
(4424018384, 4424018384, 4424018328) # augmented assignment changed a[0],b[0]
     |           |
      -----------

>>> b = a = ['hell', 'word']
>>> id(a[0]), id(b[0]), id(c[0])
(4424018328, 4424018328, 4424018328) # the same hell
     |           |           |
      -----------------------

>>> import gc
>>> gc.get_referrers(a[0]) 
[['hell', 'word'], ['hell', 'word']]  # one copy belong to a,b, the another for c
>>> gc.get_referrers(('hell'))
[['hell', 'word'], ['hell', 'word'], ('hell', None)] # ('hello', None) 
3
Dr. Hippo 2020-02-23 02:44.

Pythonでは、次のことを覚えておいてください。

    list1 = ['apples','bananas','pineapples']
    list2 = list1

List2は実際のリストを格納していませんが、list1への参照を格納しています。したがって、list1に対して何かを行うと、list2も変更されます。コピーモジュール(デフォルトではなく、pipでダウンロード)を使用して、リストの元のコピーを作成します(copy.copy()単純なリストの場合copy.deepcopy()、ネストされたリストの場合)。これにより、最初のリストで変更されないコピーが作成されます。

1
Roshin Raphel 2020-06-05 00:40.

これは、この行new_list = my_listが変数に新しい参照を割り当てるためmy_listです。new_listこれはC、以下に示すコードに似ています。

int my_list[] = [1,2,3,4];
int *new_list;
new_list = my_list;

コピーモジュールを使用して、次の方法で新しいリストを作成する必要があります。

import copy
new_list = copy.deepcopy(my_list)
0
shahar_m 2020-04-12 01:19.

ディープコピーオプションは私のために働く唯一の方法です:

from copy import deepcopy

a = [   [ list(range(1, 3)) for i in range(3) ]   ]
b = deepcopy(a)
b[0][1]=[3]
print('Deep:')
print(a)
print(b)
print('-----------------------------')
a = [   [ list(range(1, 3)) for i in range(3) ]   ]
b = a*1
b[0][1]=[3]
print('*1:')
print(a)
print(b)
print('-----------------------------')
a = [   [ list(range(1, 3)) for i in range(3) ] ]
b = a[:]
b[0][1]=[3]
print('Vector copy:')
print(a)
print(b)
print('-----------------------------')
a = [   [ list(range(1, 3)) for i in range(3) ]  ]
b = list(a)
b[0][1]=[3]
print('List copy:')
print(a)
print(b)
print('-----------------------------')
a = [   [ list(range(1, 3)) for i in range(3) ]  ]
b = a.copy()
b[0][1]=[3]
print('.copy():')
print(a)
print(b)
print('-----------------------------')
a = [   [ list(range(1, 3)) for i in range(3) ]  ]
b = a
b[0][1]=[3]
print('Shallow:')
print(a)
print(b)
print('-----------------------------')

次の出力につながります:

Deep:
[[[1, 2], [1, 2], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
*1:
[[[1, 2], [3], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
Vector copy:
[[[1, 2], [3], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
List copy:
[[[1, 2], [3], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
.copy():
[[[1, 2], [3], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------
Shallow:
[[[1, 2], [3], [1, 2]]]
[[[1, 2], [3], [1, 2]]]
-----------------------------

Related questions

MORE COOL STUFF

「水曜日」シーズン1の中心には大きなミステリーがあります

「水曜日」シーズン1の中心には大きなミステリーがあります

Netflixの「水曜日」は、典型的な10代のドラマ以上のものであり、実際、シーズン1にはその中心に大きなミステリーがあります.

ボディーランゲージの専門家は、州訪問中にカミラ・パーカー・ボウルズが輝くことを可能にした微妙なケイト・ミドルトンの動きを指摘しています

ボディーランゲージの専門家は、州訪問中にカミラ・パーカー・ボウルズが輝くことを可能にした微妙なケイト・ミドルトンの動きを指摘しています

ケイト・ミドルトンは、州の夕食会と州の訪問中にカミラ・パーカー・ボウルズからスポットライトを奪いたくなかった、と専門家は言う.

一部のファンがハリー・スタイルズとオリビア・ワイルドの「非常に友好的な」休憩が永続的であることを望んでいる理由

一部のファンがハリー・スタイルズとオリビア・ワイルドの「非常に友好的な」休憩が永続的であることを望んでいる理由

一部のファンが、オリビア・ワイルドが彼女とハリー・スタイルズとの間の「難しい」が「非常に友好的」な分割を恒久的にすることを望んでいる理由を見つけてください.

エリザベス女王の死後、ケイト・ミドルトンはまだ「非常に困難な時期」を過ごしている、と王室の専門家が明らかにする 

エリザベス女王の死後、ケイト・ミドルトンはまだ「非常に困難な時期」を過ごしている、と王室の専門家が明らかにする 

エリザベス女王の死後、ケイト・ミドルトンが舞台裏で「非常に困難な時期」を過ごしていたと伝えられている理由を調べてください.

セントヘレナのジェイコブのはしごを登るのは、気弱な人向けではありません

セントヘレナのジェイコブのはしごを登るのは、気弱な人向けではありません

セント ヘレナ島のジェイコブズ ラダーは 699 段の真っ直ぐ上る階段で、頂上に到達すると証明書が発行されるほどの難易度です。

The Secrets of Airline Travel Quiz

The Secrets of Airline Travel Quiz

Air travel is far more than getting from point A to point B safely. How much do you know about the million little details that go into flying on airplanes?

Where in the World Are You? Take our GeoGuesser Quiz

Where in the World Are You? Take our GeoGuesser Quiz

The world is a huge place, yet some GeoGuessr players know locations in mere seconds. Are you one of GeoGuessr's gifted elite? Take our quiz to find out!

バイオニック読書はあなたをより速く読むことができますか?

バイオニック読書はあなたをより速く読むことができますか?

BionicReadingアプリの人気が爆発的に高まっています。しかし、それは本当にあなたを速読術にすることができますか?

Total War:Warhammer:Kotakuレビュー

Total War:Warhammer:Kotakuレビュー

私はこのゲームを嫌う準備ができていました。先週の前に、Total War:Warhammerについての私の考えがありました:それでもここに私は、私の手にある完成品であり、私は変わった男です。

涙の道:軍事化された帝国主義勢力がスタンディングロックキャンプを占領

涙の道:軍事化された帝国主義勢力がスタンディングロックキャンプを占領

スタンディングロックスー族のメンバーと水の保護者は、ノースダコタ州のスタンディングロックにあるオセティサコウィンキャンプを去ります。(Twitter経由のCNNスクリーンショット)火と煙がスカイラインを覆い、スタンディングロックスー族のメンバーと水の保護者が、聖なるものを守りながら建てた家、オセティサコウィン(セブンカウンシルファイアーズ)キャンプから行進し、太鼓を打ち、歌い、祈りました。ダコタアクセスパイプラインとしても知られる「ブラックスネーク」からの土地。

シアーズとKマートはイヴァンカ・トランプの商品を自分たちで取り除いています

シアーズとKマートはイヴァンカ・トランプの商品を自分たちで取り除いています

写真:APシアーズとKマートは、イヴァンカ・トランプのトランプホームアイテムのコレクションも、誰も購入したくないために削除しました。シアーズとKマートの両方の親会社であるシアーズホールディングスは、土曜日のABCニュースへの声明で、彼らが気にかけていると辛抱強く説明しましたトランプラインを売り続けるにはお金を稼ぐことについてあまりにも多く。

ポテトチップスでたった10分でスペインのトルティーヤを作る

ポテトチップスでたった10分でスペインのトルティーヤを作る

伝統的なスペインのトルティーヤは通常、オリーブオイルで柔らかくなるまで調理されたポテトから始まります(30分以上かかる場合があります)が、ケトルで調理されたポテトチップスの助けを借りてわずか10分でテーブルに置くことができます。上のビデオはすべてがバラバラにならないように裏返す方法を含め、レシピ全体を説明しますが、必要なのは4〜5個の卵と3カップのケトルチップスだけです。

ケイト・ミドルトンとウィリアム王子は、彼らが子供たちと行っているスパイをテーマにした活動を共有しています

ケイト・ミドルトンとウィリアム王子は、彼らが子供たちと行っているスパイをテーマにした活動を共有しています

ケイト・ミドルトンとウィリアム王子は、子供向けのパズルの本の序文を書き、ジョージ王子、シャーロット王女、ルイ王子と一緒にテキストを読むと述べた.

事故で押しつぶされたスイカは、動物を喜ばせ水分補給するために野生生物保護団体に寄付されました

事故で押しつぶされたスイカは、動物を喜ばせ水分補給するために野生生物保護団体に寄付されました

Yak's Produce は、数十個のつぶれたメロンを野生動物のリハビリ専門家であるレスリー グリーンと彼女のルイジアナ州の救助施設で暮らす 42 匹の動物に寄付しました。

デミ・ロヴァートは、新しいミュージシャンのボーイフレンドと「幸せで健康的な関係」にあります: ソース

デミ・ロヴァートは、新しいミュージシャンのボーイフレンドと「幸せで健康的な関係」にあります: ソース

8 枚目のスタジオ アルバムのリリースに向けて準備を進めているデミ ロヴァートは、「スーパー グレート ガイ」と付き合っている、と情報筋は PEOPLE に確認しています。

Plathville の Kim と Olivia Plath が数年ぶりに言葉を交わすことへようこそ

Plathville の Kim と Olivia Plath が数年ぶりに言葉を交わすことへようこそ

イーサン プラスの誕生日のお祝いは、TLC のウェルカム トゥ プラスビルのシーズン 4 のフィナーレで、戦争中の母親のキム プラスと妻のオリビア プラスを結びつけました。

仕事の生産性を高める 8 つのシンプルなホーム オフィスのセットアップのアイデア

仕事の生産性を高める 8 つのシンプルなホーム オフィスのセットアップのアイデア

ホームオフィスのセットアップ術を極めよう!AppExert の開発者は、家族全員が一緒にいる場合でも、在宅勤務の技術を習得しています。祖父や曽祖父が共同家族で暮らしていた頃の記憶がよみがえりました。

2022 年、私たちのデジタル ライフはどこで終わり、「リアル ライフ」はどこから始まるのでしょうか?

20 年前のタイムトラベラーでさえ、日常生活におけるデジタルおよびインターネットベースのサービスの重要性に驚くことでしょう。MySpace、eBay、Napster などのプラットフォームは、高速化に焦点を合わせた世界がどのようなものになるかを示してくれました。

ニューロマーケティングの秘密科学

ニューロマーケティングの秘密科学

マーケティング担当者が人間の欲望を操作するために使用する、最先端の (気味が悪いと言う人もいます) メソッドを探ります。カートをいっぱいにして 3 桁の領収書を持って店を出る前に、ほんの数点の商品を買いに行ったことはありませんか? あなたは一人じゃない。

地理情報システムの日: GIS 開発者として学ぶべき最高の技術スタック

地理情報システムの日: GIS 開発者として学ぶべき最高の技術スタック

私たちが住んでいる世界を確実に理解するには、データが必要です。ただし、空間参照がない場合、このデータは地理的コンテキストがないと役に立たなくなる可能性があります。

Language