파이썬

[python] 할당 후 예기치 않게 변경되지 않도록 목록을 복제하려면 어떻게 해야 합니까?

zooheon 2022. 8. 22. 22:57
반응형

를 사용하는 동안 new_list = my_list모든 수정 사항은 매번 new_list변경 됩니다. my_list그 이유는 무엇이며 목록을 복제하거나 복사하여 이를 방지하려면 어떻게 해야 합니까?

 

new_list = my_list실제로 두 번째 목록을 생성하지 않습니다. 할당은 실제 목록이 아닌 목록에 대한 참조를 복사하므로 둘 다 할당 후 동일한 목록을 참조합니다 new_list.my_list

실제로 목록을 복사하려면 다음과 같은 몇 가지 옵션이 있습니다.

  • 내장 list.copy()메서드를 사용할 수 있습니다(Python 3.3부터 사용 가능):

    new_list = old_list.copy()
    
  • 다음과 같이 슬라이스할 수 있습니다.

    new_list = old_list[:]
    

    이것에 대한 Alex Martelli 의 의견(적어도 2007년에는 )은 이것이 이상한 구문이고 그것을 사용하는 것이 말이 되지 않는다는 것입니다 . ;) (그의 의견으로는 다음 것이 더 읽기 쉽습니다).

  • 내장 list()생성자를 사용할 수 있습니다.

    new_list = list(old_list)
    
  • 다음 을 사용할 수 있습니다 copy.copy().

    import copy
    new_list = copy.copy(old_list)
    

    list()이것은 먼저 데이터 유형을 찾아야 하기 때문에 보다 약간 느립니다 old_list.

  • 목록의 요소도 복사해야 하는 경우에는 다음을 사용 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 f'Foo({self.val!r})'

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(f'original: {a}\nlist.copy(): {b}\nslice: {c}\nlist(): {d}\ncopy: {e}\ndeepcopy: {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)]

 

Felix는 이미 훌륭한 답변을 제공했지만 다양한 방법의 속도 비교를 할 것이라고 생각했습니다.

  1. 10.59초(105.9µs/itn) - copy.deepcopy(old_list)
  2. 10.16초(101.6 µs/itn) - Copy()deepcopy를 사용하여 클래스를 복사하는 순수 Python 메서드
  3. 1.488초(14.88µs/itn) - Copy()클래스를 복사하지 않는 순수한 Python 메서드(dicts/lists/tuple만 해당)
  4. 0.325초(3.25µs/itn) -for item in old_list: new_list.append(item)
  5. 0.217초(2.17µs/itn) - [i for i in old_list]( 목록 이해 )
  6. 0.186초(1.86µs/itn) -copy.copy(old_list)
  7. 0.075초(0.75µs/itn) -list(old_list)
  8. 0.053초(0.53µs/itn) -new_list = []; new_list.extend(old_list)
  9. 0.039초(0.39µs/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

 

나는 Python 3.3+ 에서 슬라이싱만큼 빨라야 하는 메서드를 추가 한다고 들었 습니다.list.copy()

newlist = old_list.copy()

 

반응형