[python] 목록을 복제하거나 복사하는 방법?

파이썬에서 목록을 복제하거나 복사하는 옵션은 무엇입니까?

를 사용하는 동안 매번 변경 사항이 new_list = my_list수정됩니다 . 왜 이런거야?new_listmy_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)
  • 당신은 generic을 사용할 수 있습니다 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)]


답변

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

  1. 10.59 초 (105.9us / itn)- copy.deepcopy(old_list)
  2. 10.16 초 (101.6us / itn)-순수 파이썬 Copy() 복사로 클래스를 복사하는 메소드
  3. 1.488 초 (14.88us / itn)-순수 파이썬 Copy() 클래스를 복사하지 않는 메소드 (dicts / lists / tuples 만)
  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()목록에있는 목록, 사전 및 클래스 인스턴스를 복사하지 않고 파이썬 버전은 원본이 변경되면, 그래서 그들도 그 반대 복사 목록에 변경됩니다.

(누군가 관심이 있거나 문제를 제기하려는 경우 스크립트는 다음과 같습니다.)

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


답변

내가 한 말되었다 파이썬 3.3 이상이 있음을 추가list.copy() 슬라이스 한 빨리해야한다 방법을 :

newlist = old_list.copy()


답변

파이썬에서 목록을 복제하거나 복사하는 옵션은 무엇입니까?

Python 3에서는 다음을 사용하여 얕은 사본을 만들 수 있습니다.

a_copy = a_list.copy()

파이썬 2와 3에서는 원본의 전체 조각으로 얕은 사본을 얻을 수 있습니다.

a_copy = a_list[:]

설명

리스트를 복사하는 두 가지 의미 론적 방법이 있습니다. 얕은 사본은 동일한 오브젝트의 새 목록을 작성하고 깊은 사본은 새로운 동등한 오브젝트를 포함하는 새 목록을 작성합니다.

얕은 목록 사본

단순 복사본은 목록 자체 만 복사합니다. 목록 자체는 목록의 개체에 대한 참조 컨테이너입니다. 포함 된 개체가 변경 가능하고 하나가 변경되면 변경 내용이 두 목록에 모두 반영됩니다.

파이썬 2와 3에서는 이것을하는 다른 방법이 있습니다. 파이썬 2는 파이썬 3에서도 작동합니다.

파이썬 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

파이썬 3

파이썬 3에서리스트는 list.copy메소드를 얻습니다 :

a_copy = a_list.copy()

파이썬 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 또는 3에서 목록deepcopycopy깊은 사본 을 만들려면 모듈 에서 사용 하십시오 .

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 비트 Python 2.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 비트 Python 3.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


답변

올바른 사본을 만드는 방법을 알려주는 많은 답변이 이미 있지만, 왜 ‘원본’복사가 실패했는지는 아무도 대답하지 않습니다.

파이썬은 변수에 값을 저장하지 않습니다. 이름을 객체에 바인딩합니다. 원래 과제는 참조한 객체를 가져 와서 my_list바인딩했습니다 new_list. 어떤 이름을 사용하든 여전히 하나의 목록 만 있으므로이를 참조 할 때 작성된 변경 사항은로 참조 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)

복사시 코너 케이스에 대한 자세한 내용은 설명서 를 참조하십시오 .


답변

사용하다 thing[:]

>>> a = [1,2]
>>> b = a[:]
>>> a += [3]
>>> a
[1, 2, 3]
>>> b
[1, 2]
>>> 


답변

처음부터 시작하여이 질문을 살펴 봅시다.

따라서 두 개의 목록이 있다고 가정 해 봅시다.

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

그리고 이제 첫 번째 목록부터 시작하여 두 목록을 모두 복사해야합니다.

먼저 변수 copy를 원래 목록 으로 설정해 봅시다 list_1.

copy=list_1

이제 copy가 list_1을 복사했다고 생각하면 잘못되었습니다. 이 id함수는 두 변수가 같은 객체를 가리킬 수 있는지 보여줍니다. 이것을 시도하자 :

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

출력은 다음과 같습니다.

4329485320
4329485320

두 변수는 모두 동일한 인수입니다. 너 놀랐 니?

파이썬이 변수에 아무것도 저장하지 않는다는 것을 알기 때문에 변수는 객체를 참조하고 객체는 값을 저장합니다. 여기 객체는list 이지만 두 개의 다른 변수 이름으로 동일한 객체에 대한 두 개의 참조를 만들었습니다. 이것은 두 변수가 다른 이름을 가진 동일한 객체를 가리키고 있음을 의미합니다.

당신이 할 때 copy=list_1실제로하고 있습니다 :

여기에 이미지 설명을 입력하십시오

여기에 이미지 list_1과 copy는 두 개의 변수 이름이지만 객체는 두 변수에 대해 동일합니다. list

따라서 복사 된 목록을 수정하려고하면 목록이 하나이기 때문에 원본 목록도 수정됩니다. 복사 된 목록 또는 원본 목록에서 수행 한 목록에 관계없이 해당 목록을 수정합니다.

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']]

보시다시피 원래 중첩 목록은 수정하지 않고 복사 된 목록 만 수정했습니다.